From a4b5718a78073c665fc86a27ea126c2234fcf1a4 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 14 Jul 2011 15:31:00 +0100 Subject: [PATCH] streamline and test transaction_manager --- Makefile | 19 ++++-- transaction_manager.h | 38 ++++++----- transaction_manager.tcc | 141 +++++++++++++++++++-------------------- transaction_manager_t.cc | 91 +++++++++++++++++++++++++ 4 files changed, 194 insertions(+), 95 deletions(-) create mode 100644 transaction_manager_t.cc diff --git a/Makefile b/Makefile index 02b621a..0817b43 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,21 @@ SOURCE=\ PROGRAM_SOURCE=\ block_t.cc \ - space_map_t.cc + space_map_t.cc \ + transaction_manager_t.cc OBJECTS=$(subst .cc,.o,$(SOURCE)) CPPFLAGS=-Wall -std=c++0x INCLUDES= LIBS=-lstdc++ +.PHONEY: unit-tests + +unit-tests: block_t space_map_t transaction_manager_t + ./block_t + ./space_map_t + ./transaction_manager_t + .SUFFIXES: .cc .o .d %.d: %.cc @@ -21,13 +29,16 @@ LIBS=-lstdc++ g++ -c $(CPPFLAGS) $(INCLUDES) -o $@ $< multisnap_display: $(OBJECTS) main.o - g++ -o $@ $+ $(LIBS) + g++ $(CPPFLAGS) -o $@ $+ $(LIBS) block_t: block_t.o - g++ -o $@ $+ $(LIBS) + g++ $(CPPFLAGS) -o $@ $+ $(LIBS) space_map_t: space_map_t.o - g++ -o $@ $+ $(LIBS) + g++ $(CPPFLAGS) -o $@ $+ $(LIBS) + +transaction_manager_t: transaction_manager_t.o + g++ $(CPPFLAGS) -o $@ $+ $(LIBS) include $(subst .cc,.d,$(SOURCE)) include $(subst .cc,.d,$(PROGRAM_SOURCE)) \ No newline at end of file diff --git a/transaction_manager.h b/transaction_manager.h index 91ab028..2ff65c3 100644 --- a/transaction_manager.h +++ b/transaction_manager.h @@ -14,37 +14,41 @@ namespace persistent_data { class transaction_manager : public boost::noncopyable { public: typedef boost::shared_ptr > ptr; + typedef typename block_manager::read_ref read_ref; + typedef typename block_manager::write_ref write_ref; + typedef typename block_manager::validator::ptr validator; + // If the space map is persistent, then the caller should + // hold onto a reference and remember to call sm_->commit() + // and update the superblock before dropping the superblock + // reference. transaction_manager(typename block_manager::ptr bm, space_map::ptr sm); ~transaction_manager(); - typedef typename block_manager::read_ref read_ref; - typedef typename block_manager::write_ref write_ref; - typedef typename block_manager::block_validator block_validator; + // Drop the superblock reference to commit + write_ref begin(block_address superblock); + write_ref begin(block_address superblock, validator v); - void reserve_block(block_address location); - void begin(); - void pre_commit(); - void commit(write_ref superblock); - - block_address alloc_block(); write_ref new_block(); - write_ref new_block(block_validator const &v); + write_ref new_block(validator v); - write_ref shadow(block_address orig, bool &inc_children); - write_ref shadow(block_address orig, block_validator const &v, bool &inc_children); + // shadowing returns a new write_ref, and a boolean which + // indicates whether the children should be incremented. + std::pair shadow(block_address orig); + std::pair shadow(block_address orig, validator v); read_ref read_lock(block_address b); - read_ref read_lock(block_address b, block_validator const &v); + read_ref read_lock(block_address b, validator v); - void inc(block_address b); - void dec(block_address b); - uint32_t ref_count(block_address b) const; + space_map::ptr get_sm() { + return sm_; + } private: void add_shadow(block_address b); - bool is_shadow(block_address b); + void remove_shadow(block_address b); + bool is_shadow(block_address b) const; void wipe_shadow_table(); typename block_manager::ptr bm_; diff --git a/transaction_manager.tcc b/transaction_manager.tcc index e6b755c..2a42ae6 100644 --- a/transaction_manager.tcc +++ b/transaction_manager.tcc @@ -4,6 +4,7 @@ using namespace boost; using namespace persistent_data; +using namespace std; //---------------------------------------------------------------- @@ -21,98 +22,104 @@ transaction_manager::~transaction_manager() } template -void -transaction_manager::reserve_block(block_address location) +typename transaction_manager::write_ref +transaction_manager::begin(block_address superblock) { - sm_->inc_block(location); -} - -template -void -transaction_manager::begin() -{ -} - -template -void -transaction_manager::pre_commit() -{ - sm_->commit(); -} - -template -void -transaction_manager::commit(write_ref superblock) -{ - bm_->flush(superblock); + auto wr = bm_->superblock(superblock); wipe_shadow_table(); + return wr; } template -block_address -transaction_manager::alloc_block() +typename transaction_manager::write_ref +transaction_manager::begin(block_address superblock, + validator v) { - return sm_->new_block(); + auto wr = bm_->superblock(superblock, v); + wipe_shadow_table(); + return wr; } +// FIXME: these explicit try/catches are gross template typename transaction_manager::write_ref transaction_manager::new_block() { block_address b = sm_->new_block(); - add_shadow(b); - return bm_->write_lock_zero(b); + try { + add_shadow(b); + try { + return bm_->write_lock_zero(b); + } catch (...) { + remove_shadow(b); + throw; + } + + } catch (...) { + sm_->dec(b); + throw; + } } template typename transaction_manager::write_ref -transaction_manager::new_block(block_validator const &v) +transaction_manager::new_block(validator v) { block_address b = sm_->new_block(); - add_shadow(b); - return bm_->write_lock_zero(b, v); + try { + add_shadow(b); + try { + return bm_->write_lock_zero(b, v); + } catch (...) { + remove_shadow(b); + throw; + } + } catch (...) { + sm_->dec(b); + throw; + } } +// FIXME: make exception safe template -typename transaction_manager::write_ref -transaction_manager::shadow(block_address orig, bool &inc_children) +pair::write_ref, bool> +transaction_manager::shadow(block_address orig) { if (is_shadow(orig) && - sm_->count_possibly_greater_than_one(orig)) { - inc_children = false; - return bm_->write_lock(orig); - } + !sm_->count_possibly_greater_than_one(orig)) + return make_pair(bm_->write_lock(orig), false); auto src = bm_->read_lock(orig); auto dest = bm_->write_lock_zero(sm_->new_block()); - ::memcpy(dest->data_, src->data_, BlockSize); + ::memcpy(dest.data(), src.data(), BlockSize); ref_t count = sm_->get_count(orig); - sm_->dec_block(orig); - inc_children = count > 1; - add_shadow(dest->location_); - return dest; + if (count == 0) + throw runtime_error("shadowing free block"); + sm_->dec(orig); + add_shadow(dest.get_location()); + return make_pair(dest, count > 1); } +// FIXME: duplicate code template -typename transaction_manager::write_ref -transaction_manager::shadow(block_address orig, block_validator const &v, bool &inc_children) +pair::write_ref, bool> +transaction_manager::shadow(block_address orig, validator v) { if (is_shadow(orig) && - sm_->count_possibly_greater_than_one(orig)) { - inc_children = false; - return bm_->write_lock(orig); - } + sm_->count_possibly_greater_than_one(orig)) + return make_pair(bm_->write_lock(orig), false); auto src = bm_->read_lock(orig, v); auto dest = bm_->write_lock_zero(sm_->new_block(), v); ::memcpy(dest->data_, src->data_, BlockSize); ref_t count = sm_->get_count(orig); - sm_->dec_block(orig); - inc_children = count > 1; + if (count == 0) + throw runtime_error("shadowing free block"); + sm_->dec(orig); add_shadow(dest->location_); - return dest; + return make_pair(dest, count > 1); } template @@ -124,32 +131,11 @@ transaction_manager::read_lock(block_address b) template typename transaction_manager::read_ref -transaction_manager::read_lock(block_address b, block_validator const &v) +transaction_manager::read_lock(block_address b, validator v) { return bm_->read_lock(b, v); } -template -void -transaction_manager::inc(block_address b) -{ - sm_->inc_block(b); -} - -template -void -transaction_manager::dec(block_address b) -{ - sm_->dec_block(b); -} - -template -uint32_t -transaction_manager::ref_count(block_address b) const -{ - return sm_->get_count(b); -} - template void transaction_manager::add_shadow(block_address b) @@ -157,9 +143,16 @@ transaction_manager::add_shadow(block_address b) shadows_.insert(b); } +template +void +transaction_manager::remove_shadow(block_address b) +{ + shadows_.erase(b); +} + template bool -transaction_manager::is_shadow(block_address b) +transaction_manager::is_shadow(block_address b) const { return shadows_.count(b) > 0; } diff --git a/transaction_manager_t.cc b/transaction_manager_t.cc new file mode 100644 index 0000000..98912ae --- /dev/null +++ b/transaction_manager_t.cc @@ -0,0 +1,91 @@ +#include "transaction_manager.h" +#include "core_map.h" + +#define BOOST_TEST_MODULE TransactionManagerTests +#include + +using namespace std; +using namespace boost; +using namespace persistent_data; + +//---------------------------------------------------------------- + +namespace { + block_address const NR_BLOCKS = 1024; + + transaction_manager<4096>::ptr + create_tm() { + block_manager<4096>::ptr bm(new block_manager<4096>("./test.data", NR_BLOCKS)); + space_map::ptr sm(new core_map(NR_BLOCKS)); + transaction_manager<4096>::ptr tm(new transaction_manager<4096>(bm, sm)); + return tm; + } +} + +//---------------------------------------------------------------- + +BOOST_AUTO_TEST_CASE(commit_succeeds) +{ + auto tm = create_tm(); + tm->begin(0); +} + +BOOST_AUTO_TEST_CASE(shadowing) +{ + auto tm = create_tm(); + auto superblock = tm->begin(0); + + auto sm = tm->get_sm(); + sm->inc(1); + auto p = tm->shadow(1); + auto b = p.first.get_location(); + BOOST_CHECK(b != 1); + BOOST_CHECK(!p.second); + BOOST_CHECK(sm->get_count(1) == 0); + + p = tm->shadow(b); + BOOST_CHECK(p.first.get_location() == b); + BOOST_CHECK(!p.second); + + sm->inc(b); + p = tm->shadow(b); + BOOST_CHECK(p.first.get_location() != b); + BOOST_CHECK(p.second); +} + +BOOST_AUTO_TEST_CASE(multiple_shadowing) +{ + auto tm = create_tm(); + auto superblock = tm->begin(0); + + auto sm = tm->get_sm(); + sm->set_count(1, 3); + + auto p = tm->shadow(1); + auto b = p.first.get_location(); + BOOST_CHECK(b != 1); + BOOST_CHECK(p.second); + + p = tm->shadow(1); + auto b2 = p.first.get_location(); + BOOST_CHECK(b2 != 1); + BOOST_CHECK(b2 != b); + BOOST_CHECK(p.second); + + p = tm->shadow(1); + auto b3 = p.first.get_location(); + BOOST_CHECK(b3 != b2); + BOOST_CHECK(b3 != b); + BOOST_CHECK(b3 != 1); + BOOST_CHECK(!p.second); +} + +BOOST_AUTO_TEST_CASE(shadow_free_block_fails) +{ + auto tm = create_tm(); + auto superblock = tm->begin(0); + BOOST_CHECK_THROW(tm->shadow(1), runtime_error); +} + +// First shadow in a transaction returns a different block +// Second shadow in a transaction rtu