From 8a7f0214d3c04096d82c0b5fcc48b1e38fd02f4d Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 13 Jul 2011 15:09:33 +0100 Subject: [PATCH] block_manager and unit test --- Makefile | 12 +++-- block.h | 40 +++++++++-------- block.tcc | 75 ++++++++++++++++++++++---------- block_t.cc | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+), 44 deletions(-) create mode 100644 block_t.cc diff --git a/Makefile b/Makefile index a3cddf4..5046a8c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ SOURCE=\ - main.cc \ metadata.cc +PROGRAM_SOURCE=\ + block_t.cc + OBJECTS=$(subst .cc,.o,$(SOURCE)) CPPFLAGS=-Wall -std=c++0x INCLUDES= @@ -17,7 +19,11 @@ LIBS=-lstdc++ .cc.o: g++ -c $(CPPFLAGS) $(INCLUDES) -o $@ $< -multisnap_display: $(OBJECTS) +multisnap_display: $(OBJECTS) main.o g++ -o $@ $+ $(LIBS) -include $(subst .cc,.d,$(SOURCE)) \ No newline at end of file +block_t: block_t.o + g++ -o $@ $+ $(LIBS) + +include $(subst .cc,.d,$(SOURCE)) +include $(subst .cc,.d,$(PROGRAM_SOURCE)) \ No newline at end of file diff --git a/block.h b/block.h index 7ca2d41..593325b 100644 --- a/block.h +++ b/block.h @@ -20,7 +20,7 @@ namespace persistent_data { public: typedef boost::shared_ptr ptr; - block_manager(std::string const &path); + block_manager(std::string const &path, block_address nr_blocks); ~block_manager(); typedef unsigned char buffer[BlockSize]; @@ -28,48 +28,50 @@ namespace persistent_data { class block; - class block_validator { + class validator { public: - typedef boost::shared_ptr ptr; + typedef boost::shared_ptr ptr; - virtual ~block_validator() {} + virtual ~validator() {} virtual void check(block const &b) const = 0; virtual void prepare(block &b) const = 0; }; struct block { - typedef boost::optional maybe_validator; + typedef boost::shared_ptr ptr; + typedef boost::optional maybe_validator; block(block_address location, maybe_validator v = maybe_validator()) : location_(location), - validator_(v) { + validator_(v), + initialised_(false) { } block_address location_; buffer data_; maybe_validator validator_; + bool initialised_; }; - typedef boost::shared_ptr block_ptr; class read_ref { public: - read_ref(block_ptr b); + read_ref(typename block::ptr b); virtual ~read_ref() {} block_address get_location() const; const_buffer &data() const; protected: - block_ptr block_; + typename block::ptr block_; }; // Inherited from read_ref, since you can read a block that's write // locked. class write_ref : public read_ref { public: - write_ref(block_ptr b); + write_ref(typename block::ptr b); using read_ref::data; buffer &data(); @@ -77,10 +79,10 @@ namespace persistent_data { // Locking methods read_ref - read_lock(block_address location); + read_lock(block_address location) const; boost::optional - read_try_lock(block_address location); + read_try_lock(block_address location) const; write_ref write_lock(block_address location); @@ -91,31 +93,33 @@ namespace persistent_data { // Validator variants read_ref read_lock(block_address location, - typename block_validator::ptr const &v); + typename validator::ptr const &v) const; boost::optional read_try_lock(block_address location, - typename block_validator::ptr const &v); + typename validator::ptr const &v) const; write_ref write_lock(block_address location, - typename block_validator::ptr const &v); + typename validator::ptr const &v); write_ref write_lock_zero(block_address location, - typename block_validator::ptr const &v); + typename validator::ptr const &v); // Use this to commit changes void flush(write_ref super_block); private: - void read_block(block &b); + void check(block_address b) const; + + void read_block(block &b) const; void write_block(block const &b); void zero_block(block &b); - void write_and_release(block *b); int fd_; + block_address nr_blocks_; }; } diff --git a/block.tcc b/block.tcc index 2547610..d2cedb4 100644 --- a/block.tcc +++ b/block.tcc @@ -16,7 +16,7 @@ using namespace std; //---------------------------------------------------------------- template -block_manager::read_ref::read_ref(block_manager::block_ptr b) +block_manager::read_ref::read_ref(typename block_manager::block::ptr b) : block_(b) { } @@ -36,7 +36,7 @@ block_manager::read_ref::data() const } template -block_manager::write_ref::write_ref(block_manager::block_ptr b) +block_manager::write_ref::write_ref(typename block_manager::block::ptr b) : read_ref(b) { } @@ -51,9 +51,10 @@ block_manager::write_ref::data() //---------------------------------------------------------------- template -block_manager::block_manager(std::string const &path) +block_manager::block_manager(std::string const &path, block_address nr_blocks) + : nr_blocks_(nr_blocks) { - fd_ = ::open(path.c_str(), O_RDWR | O_EXCL); + fd_ = ::open(path.c_str(), O_RDWR | O_CREAT, 0666); if (fd_ < 0) throw std::runtime_error("couldn't open file"); } @@ -66,16 +67,18 @@ block_manager::~block_manager() template typename block_manager::read_ref -block_manager::read_lock(block_address location) +block_manager::read_lock(block_address location) const { - block_ptr b(new block(location)); + check(location); + + typename block::ptr b(new block(location)); read_block(*b); return read_ref(b); } template optional::read_ref> -block_manager::read_try_lock(block_address location) +block_manager::read_try_lock(block_address location) const { return read_lock(location); } @@ -84,7 +87,9 @@ template typename block_manager::write_ref block_manager::write_lock(block_address location) { - block_ptr b(new block(location), bind(&block_manager::write_and_release, this, _1)); + check(location); + + typename block::ptr b(new block(location), bind(&block_manager::write_and_release, this, _1)); read_block(*b); return write_ref(b); } @@ -93,7 +98,9 @@ template typename block_manager::write_ref block_manager::write_lock_zero(block_address location) { - block_ptr b(new block(location), bind(&block_manager::write_and_release, this, _1)); + check(location); + + typename block::ptr b(new block(location), bind(&block_manager::write_and_release, this, _1)); zero_block(*b); return write_ref(b); } @@ -101,9 +108,11 @@ block_manager::write_lock_zero(block_address location) template typename block_manager::read_ref block_manager::read_lock(block_address location, - typename block_manager::block_validator::ptr const &v) + typename block_manager::validator::ptr const &v) const { - block_ptr b(new block(location, v)); + check(location); + + typename block::ptr b(new block(location, v)); read_block(*b); return read_ref(b); } @@ -111,7 +120,7 @@ block_manager::read_lock(block_address location, template optional::read_ref> block_manager::read_try_lock(block_address location, - typename block_manager::block_validator::ptr const &v) + typename block_manager::validator::ptr const &v) const { return read_lock(location, v); } @@ -119,9 +128,12 @@ block_manager::read_try_lock(block_address location, template typename block_manager::write_ref block_manager::write_lock(block_address location, - typename block_manager::block_validator::ptr const &v) + typename block_manager::validator::ptr const &v) { - block_ptr b(new block(location, v), write_and_release); + check(location); + + typename block::ptr b(new block(location, v), + bind(&block_manager::write_and_release, this, _1)); read_block(*b); return write_ref(b); } @@ -129,9 +141,12 @@ block_manager::write_lock(block_address location, template typename block_manager::write_ref block_manager::write_lock_zero(block_address location, - typename block_manager::block_validator::ptr const &v) + typename block_manager::validator::ptr const &v) { - block_ptr b(new block(location, v), write_and_release); + check(location); + + typename block::ptr b(new block(location, v), + bind(&block_manager::write_and_release, this, _1)); zero_block(*b); return write_ref(b); } @@ -140,16 +155,16 @@ template void block_manager::flush(block_manager::write_ref super_block) { + // FIXME: the caller still holds the write_ref, so the superblock + // will get written twice write_block(super_block); ::fsync(fd_); } template void -block_manager::read_block(block &b) +block_manager::read_block(block &b) const { - std::cerr << "reading block: " << b.location_ << std::endl; - off_t r; r = ::lseek(fd_, BlockSize * b.location_, SEEK_SET); if (r == (off_t) -1) @@ -168,15 +183,14 @@ block_manager::read_block(block &b) if (n < 0) throw std::runtime_error("read failed"); + + b.initialised_ = true; } template void block_manager::write_block(block const &b) { - - std::cerr << "writing block: " << b.location_ << std::endl; - off_t r; r = ::lseek(fd_, BlockSize * b.location_, SEEK_SET); if (r == (off_t) -1) @@ -202,14 +216,29 @@ void block_manager::zero_block(block &b) { memset(b.data_, 0, BlockSize); + b.initialised_ = true; } template void block_manager::write_and_release(block *b) { - write_block(*b); + if (b->initialised_) { + if (b->validator_) + (*b->validator_)->prepare(*b); + + write_block(*b); + } + delete b; } +template +void +block_manager::check(block_address b) const +{ + if (b >= nr_blocks_) + throw std::runtime_error("block address out of bounds"); +} + //---------------------------------------------------------------- diff --git a/block_t.cc b/block_t.cc new file mode 100644 index 0000000..5ce82d7 --- /dev/null +++ b/block_t.cc @@ -0,0 +1,125 @@ +#include "block.h" + +#include + +#define BOOST_TEST_MODULE BlockManagerTests +#include + +using namespace std; + +//---------------------------------------------------------------- + +namespace { + template + void check_all_bytes(typename block_manager::read_ref const &rr, int v) { + auto data = rr.data(); + for (unsigned b = 0; b < BlockSize; b++) + BOOST_CHECK_EQUAL(data[b], v); + } + + template + class zero_validator : public block_manager::validator { + void check(block_manager<4096>::block const &blk) const { + for (unsigned b = 0; b < BlockSize; b++) + if (blk.data_[b] != 0) + throw runtime_error("validator check zero"); + } + + void prepare(block_manager<4096>::block &blk) const { + for (unsigned b = 0; b < BlockSize; b++) + blk.data_[b] = 0; + } + }; +} + +//---------------------------------------------------------------- + +BOOST_AUTO_TEST_CASE(bad_path) +{ + BOOST_CHECK_THROW(block_manager<4096>("/bogus/bogus/bogus", 1234), runtime_error); +} + +BOOST_AUTO_TEST_CASE(out_of_range_access) +{ + block_manager<4096> bm("./test.data", 1024); + BOOST_CHECK_THROW(bm.read_lock(1024), runtime_error); +} + +BOOST_AUTO_TEST_CASE(read_lock_all_blocks) +{ + block_address const nr = 64; + block_manager<4096> bm("./test.data", nr); + + for (unsigned i = 0; i < nr; i++) + bm.read_lock(i); +} + +BOOST_AUTO_TEST_CASE(write_lock_all_blocks) +{ + block_address const nr = 64; + block_manager<4096> bm("./test.data", nr); + + for (unsigned i = 0; i < nr; i++) + bm.write_lock(i); +} + +BOOST_AUTO_TEST_CASE(writes_persist) +{ + block_address const nr = 64; + block_manager<4096> bm("./test.data", nr); + + for (unsigned i = 0; i < nr; i++) { + auto wr = bm.write_lock(i); + ::memset(wr.data(), i, 4096); + } + + for (unsigned i = 0; i < nr; i++) { + auto rr = bm.read_lock(i); + check_all_bytes<4096>(rr, i % 256); + } +} + +BOOST_AUTO_TEST_CASE(write_lock_zero_zeroes) +{ + block_address const nr = 64; + block_manager<4096> bm("./test.data", nr); + check_all_bytes<4096>(bm.write_lock_zero(23), 0); +} + +BOOST_AUTO_TEST_CASE(different_block_sizes) +{ + { + block_manager<4096> bm("./test.data", 64); + auto rr = bm.read_lock(0); + BOOST_CHECK_EQUAL(sizeof(rr.data()), 4096); + } + + + { + block_manager<64 * 1024> bm("./test.data", 64); + auto rr = bm.read_lock(0); + BOOST_CHECK_EQUAL(sizeof(rr.data()), 64 * 1024); + } +} + +BOOST_AUTO_TEST_CASE(read_validator_works) +{ + typename block_manager<4096>::block_manager::validator::ptr v(new zero_validator<4096>()); + block_manager<4096> bm("./test.data", 64); + bm.write_lock_zero(0); + bm.read_lock(0, v); +} + +BOOST_AUTO_TEST_CASE(write_validator_works) +{ + typename block_manager<4096>::block_manager::validator::ptr v(new zero_validator<4096>()); + block_manager<4096> bm("./test.data", 64); + { + auto wr = bm.write_lock(0, v); + ::memset(wr.data(), 23, sizeof(wr.data())); + } + + check_all_bytes<4096>(bm.read_lock(0), 0); +} + +//----------------------------------------------------------------