Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r62360 - in sandbox/transaction: boost/transact boost/transact/detail libs/transact/example libs/transact/test/test_stm
From: strasser_at_[hidden]
Date: 2010-05-31 18:12:44


Author: stefans
Date: 2010-05-31 18:12:43 EDT (Mon, 31 May 2010)
New Revision: 62360
URL: http://svn.boost.org/trac/boost/changeset/62360

Log:
 - test/test_stm: initial
 - exampe/banking.cpp: initial
 - various bugfixes
Added:
   sandbox/transaction/libs/transact/example/banking.cpp (contents, props changed)
   sandbox/transaction/libs/transact/test/test_stm/
   sandbox/transaction/libs/transact/test/test_stm/default.hpp (contents, props changed)
   sandbox/transaction/libs/transact/test/test_stm/object.hpp (contents, props changed)
   sandbox/transaction/libs/transact/test/test_stm/test_stm.hpp (contents, props changed)
Text files modified:
   sandbox/transaction/boost/transact/basic_transaction.hpp | 5 +++--
   sandbox/transaction/boost/transact/detail/transaction_manager.hpp | 8 ++++----
   sandbox/transaction/boost/transact/simple_transaction_manager.hpp | 5 +++--
   3 files changed, 10 insertions(+), 8 deletions(-)

Modified: sandbox/transaction/boost/transact/basic_transaction.hpp
==============================================================================
--- sandbox/transaction/boost/transact/basic_transaction.hpp (original)
+++ sandbox/transaction/boost/transact/basic_transaction.hpp 2010-05-31 18:12:43 EDT (Mon, 31 May 2010)
@@ -83,10 +83,11 @@
     /// Throws: \c no_transaction_manager, \c io_failure, \c thread_resource_error,
     /// resource-specific exceptions thrown by resource managers restarting transactions.
     /// \brief Restarts the transactions
+ /// \pre This transaction must neither be committed nor rolled back
     void restart(){
- if(!this->done) this->rollback();
+ //do not assert the !this->done to test the precondition. restart() is ok
+ //after commit() has failed with an isolation_exception.
         TxMgr::restart_transaction(this->tx);
- this->done=false;
     }
 
     /// Throws: thread_resource_error

Modified: sandbox/transaction/boost/transact/detail/transaction_manager.hpp
==============================================================================
--- sandbox/transaction/boost/transact/detail/transaction_manager.hpp (original)
+++ sandbox/transaction/boost/transact/detail/transaction_manager.hpp 2010-05-31 18:12:43 EDT (Mon, 31 May 2010)
@@ -26,7 +26,7 @@
 template<class Tag,class Resource>
 class tag_resource_pair : noncopyable{
 public:
- typedef std::pair<Tag,Resource *> *iterator;
+ typedef std::pair<Tag,Resource *> const *iterator;
     void connect(Tag const &tag,Resource &res){
         if(this->pair){
             if(this->tag_is_equal(tag)) throw resource_error();
@@ -68,13 +68,13 @@
         return this->pair->first;
     }
 private:
- bool tag_is_equal(Tag const &o){
+ bool tag_is_equal(Tag const &o) const{
         return this->tag_is_equal(o,boost::is_empty<Tag>());
     }
- bool tag_is_equal(Tag const &,mpl::true_ empty){
+ bool tag_is_equal(Tag const &,mpl::true_ empty) const{
         return true;
     }
- bool tag_is_equal(Tag const &o,mpl::false_ empty){
+ bool tag_is_equal(Tag const &o,mpl::false_ empty) const{
         BOOST_ASSERT(this->pair);
         return this->pair->first == o;
     }

Modified: sandbox/transaction/boost/transact/simple_transaction_manager.hpp
==============================================================================
--- sandbox/transaction/boost/transact/simple_transaction_manager.hpp (original)
+++ sandbox/transaction/boost/transact/simple_transaction_manager.hpp 2010-05-31 18:12:43 EDT (Mon, 31 May 2010)
@@ -111,6 +111,7 @@
     void construct(resources &ress,mpl::false_ flat){
         this->construct_closed(ress);
     }
+ friend struct transact::detail::beginner<transaction,resources>;
     void construct_closed(resources &ress){
         this->rtxs_storage=in_place();
         this->rtxs=&*this->rtxs_storage;
@@ -190,11 +191,11 @@
 
     template<class Tag>
     struct resource_iterator{
- typedef typename detail::resources_type::template iterator<Tag>::type type;
+ typedef typename detail::resources_type::iterator type;
     };
     template<class Tag>
     static std::pair<typename resource_iterator<Tag>::type,typename resource_iterator<Tag>::type> resources(){
- return resources_.template range<Tag>();
+ return resources_.range();
     }
 
     static typename Resource::transaction &

Added: sandbox/transaction/libs/transact/example/banking.cpp
==============================================================================
--- (empty file)
+++ sandbox/transaction/libs/transact/example/banking.cpp 2010-05-31 18:12:43 EDT (Mon, 31 May 2010)
@@ -0,0 +1,114 @@
+// Copyright Stefan Strasser 2010.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+
+#include <libs/transact/test/test_stm/default.hpp>
+#include <boost/transact/language.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
+#include <iostream>
+#include <boost/thread.hpp>
+
+using namespace boost;
+using namespace transact;
+using namespace std;
+
+//test_stm only supports static variables, create "Accounts" of them:
+template<std::size_t N>
+struct account_tag{};
+
+template<std::size_t Accounts>
+class bank : bank<Accounts - 1>{
+ typedef bank<Accounts - 1> base_type;
+ static std::size_t const N = Accounts - 1;
+public:
+ std::size_t size() const{ return Accounts; }
+ void deposit(std::size_t accountnr,int amount){
+ if(accountnr == N) this->account+=amount;
+ else base_type::deposit(accountnr,amount);
+ }
+ void withdraw(std::size_t accountnr,int amount){
+ if(accountnr == N) this->account-=amount;
+ else base_type::withdraw(accountnr,amount);
+ }
+ int overall_balance() const{
+ return this->account + base_type::overall_balance();
+ }
+private:
+ object<int,account_tag<N> > account;
+};
+
+template<>
+struct bank<0>{
+ void deposit(std::size_t,int){
+ BOOST_ASSERT(false);
+ }
+ void withdraw(std::size_t,int){
+ BOOST_ASSERT(false);
+ }
+ int overall_balance() const{
+ return 0;
+ }
+};
+
+bank<100> mybank;
+
+struct teller{
+ teller()
+ : transactions(0)
+ , isolations(0){
+ thread(boost::bind(&teller::run,this)).swap(this->thr);
+ }
+
+ void run(){
+ while(!this_thread::interruption_requested()){
+ int amount=rand() % 1000;
+ int acc1=rand() % mybank.size();
+ int acc2=rand() % mybank.size();
+
+ begin_transaction{
+ mybank.withdraw(acc1,amount);
+ mybank.deposit(acc2,amount);
+ }retry{
+ ++isolations;
+ }end_retry;
+ ++transactions;
+ }
+ }
+ int tellernr;
+ unsigned int transactions;
+ unsigned int isolations;
+ thread thr;
+};
+
+
+int main(){
+ test_stm rm;
+ transaction_manager::connect_resource(rm);
+
+ static unsigned int const nr_of_threads=10;
+
+ cerr << "overall balance before: " << mybank.overall_balance() << endl;
+
+ typedef ptr_vector<teller> tellers_type;
+ tellers_type tellers;
+ for(int c=0;c<nr_of_threads;++c){
+ tellers.push_back(new teller);
+ }
+ this_thread::sleep(posix_time::seconds(5));
+
+ unsigned int transactions=0,isolations=0;
+
+ for(tellers_type::iterator it=tellers.begin();it != tellers.end();++it){
+ it->thr.interrupt();
+ it->thr.join();
+ transactions+=it->transactions;
+ isolations+=it->isolations;
+ }
+ cerr << "overall balance after: " << mybank.overall_balance() << endl;
+ cerr << transactions << " transactions, " << float(isolations)/transactions*100 << "% isolation exceptions" << endl;
+}
+
+

Added: sandbox/transaction/libs/transact/test/test_stm/default.hpp
==============================================================================
--- (empty file)
+++ sandbox/transaction/libs/transact/test/test_stm/default.hpp 2010-05-31 18:12:43 EDT (Mon, 31 May 2010)
@@ -0,0 +1,19 @@
+// Copyright Stefan Strasser 2010.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef BOOST_TRANSACT_TEST_STM_DEFAULT_HPP
+#define BOOST_TRANSACT_TEST_STM_DEFAULT_HPP
+
+#include <boost/transact/simple_transaction_manager.hpp>
+#include <libs/transact/test/test_stm/test_stm.hpp>
+
+
+#define BOOST_TRANSACT_CONFIGURATION \
+ boost::transact::simple_transaction_manager<test_stm>
+
+#include <libs/transact/test/test_stm/object.hpp>
+
+
+#endif

Added: sandbox/transaction/libs/transact/test/test_stm/object.hpp
==============================================================================
--- (empty file)
+++ sandbox/transaction/libs/transact/test/test_stm/object.hpp 2010-05-31 18:12:43 EDT (Mon, 31 May 2010)
@@ -0,0 +1,52 @@
+// Copyright Stefan Strasser 2010.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef BOOST_TRANSACT_TEST_STM_OBJECT_HPP
+#define BOOST_TRANSACT_TEST_STM_OBJECT_HPP
+
+#include <boost/transact/default_tag.hpp>
+
+template<class T,class Tag,class TxMgr,class ResourceTag=boost::transact::default_tag>
+class basic_object{
+public:
+ explicit basic_object(ResourceTag const &tag=ResourceTag()) : tag(tag){}
+ operator T() const{
+ if(typename TxMgr::transaction *tx=TxMgr::current_transaction()){
+ return TxMgr::resource(this->tag).template read<T,Tag>(TxMgr::resource_transaction(*tx,this->tag));
+ }else return TxMgr::resource(this->tag).template read<T,Tag>();
+ }
+ basic_object &operator=(T const &t){
+ typename TxMgr::transaction *tx=TxMgr::current_transaction();
+ if(!tx) throw boost::transact::no_transaction();
+ TxMgr::resource(this->tag).template write<T,Tag>(TxMgr::resource_transaction(*tx,this->tag),t);
+ return *this;
+ }
+ template<class T2>
+ basic_object &operator+=(T2 const &t){
+ return *this = *this + t;
+ }
+ template<class T2>
+ basic_object &operator-=(T2 const &t){
+ return *this = *this - t;
+ }
+private:
+ ResourceTag tag;
+};
+
+#include <boost/transact/transaction_manager.hpp>
+
+template<class T,class Tag,class ResourceTag=boost::transact::default_tag>
+class object : public basic_object<T,Tag,boost::transact::transaction_manager,ResourceTag>{
+ typedef basic_object<T,Tag,boost::transact::transaction_manager,ResourceTag> base_type;
+public:
+ object &operator=(T const &t){
+ base_type::operator=(t);
+ return *this;
+ }
+};
+
+#endif
+
+

Added: sandbox/transaction/libs/transact/test/test_stm/test_stm.hpp
==============================================================================
--- (empty file)
+++ sandbox/transaction/libs/transact/test/test_stm/test_stm.hpp 2010-05-31 18:12:43 EDT (Mon, 31 May 2010)
@@ -0,0 +1,255 @@
+// Copyright Stefan Strasser 2010.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+//
+// horrible STM implementation, for testing purposes
+
+#ifndef BOOST_TRANSACT_TEST_STM_HPP
+#define BOOST_TRANSACT_TEST_STM_HPP
+
+#include <boost/mpl/set.hpp>
+#include <typeinfo>
+#include <map>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/locks.hpp>
+#include <boost/assert.hpp>
+#include <boost/transact/resource_manager.hpp>
+#include <boost/transact/exception.hpp>
+
+struct test_stm{
+public:
+ class transaction;
+private:
+ typedef unsigned int transaction_id;
+ typedef std::pair<test_stm const *,transaction_id> instance_id;
+ struct tx_construct_t{
+ tx_construct_t(test_stm &rm,transaction_id id,transaction *parent)
+ : rm(rm),id(id),parent(parent){}
+ test_stm &rm;
+ transaction_id id;
+ transaction *parent;
+ };
+public:
+ test_stm() : next_tid(1){}
+
+ typedef boost::mpl::set<
+ boost::transact::nested_transaction_service_tag
+ > services;
+
+ tx_construct_t begin_transaction(){
+ boost::lock_guard<mutex_type> lock(mutex);
+ return tx_construct_t(*this,this->next_tid++,0);
+ }
+ tx_construct_t begin_nested_transaction(transaction &tx){
+ boost::lock_guard<mutex_type> lock(mutex);
+ return tx_construct_t(*this,this->next_tid++,&tx);
+ }
+ void commit_transaction(transaction &tx);
+ void rollback_transaction(transaction &tx);
+
+ template<class T,class Tag>
+ T read(){
+ boost::lock_guard<mutex_type> lock(mutex);
+ return object<T,Tag>::read(*this);
+ }
+ template<class T,class Tag>
+ T read(transaction &tx);
+ template<class T,class Tag>
+ void write(transaction &tx,T const &t);
+
+ template<class T>
+ class instance;
+ template<class T,class Tag>
+ class object;
+
+ typedef boost::mutex mutex_type;
+ static mutex_type mutex;
+
+ transaction_id next_tid;
+};
+
+
+template<class T>
+class test_stm::instance{
+public:
+ instance() : value(),last_update(){}
+ instance(T const &value,transaction_id last_update) : value(value),last_update(last_update){}
+ T value;
+ transaction_id last_update;
+};
+
+class test_stm::transaction{
+public:
+ explicit transaction(tx_construct_t c)
+ : rm(c.rm)
+ , id_(c.id)
+ , parent_(c.parent)
+ , active(true){}
+ transaction_id id() const{ return this->id_; }
+ transaction *parent() const{ return this->parent_; }
+
+ void check_active(){
+ if(!this->active) throw boost::transact::no_transaction();
+ }
+
+ template<class T,class Tag>
+ void register_access(transaction_id read_tid){
+ this->accesses.push_back(new access<T,Tag>(read_tid));
+ }
+ void check(){
+ for(accesses_type::iterator it=this->accesses.begin();it != this->accesses.end();++it){
+ it->check(this->rm,*this);
+ }
+ }
+ void update(){
+ for(accesses_type::iterator it=this->accesses.begin();it != this->accesses.end();++it){
+ it->update(this->rm,*this);
+ }
+ }
+ void rollback(){
+ this->active=false;
+ }
+ ~transaction(){
+ boost::lock_guard<mutex_type> lock(mutex);
+ for(accesses_type::iterator it=this->accesses.begin();it != this->accesses.end();++it){
+ it->clear(this->rm,*this);
+ }
+ }
+private:
+ struct access_base{
+ virtual void check(test_stm const &,transaction &) const = 0;
+ virtual void update(test_stm const &,transaction &) const = 0;
+ virtual void clear(test_stm const &,transaction &) const = 0;
+ virtual ~access_base(){}
+ };
+
+ template<class T,class Tag>
+ struct access : access_base{
+ explicit access(transaction_id read_tid) : read_tid(read_tid){}
+ virtual void check(test_stm const &rm,transaction &tx) const{
+ object<T,Tag>::check(rm,tx,this->read_tid);
+ }
+ virtual void update(test_stm const &rm,transaction &tx) const{
+ object<T,Tag>::update(rm,tx);
+ }
+ virtual void clear(test_stm const &rm,transaction &tx) const{
+ object<T,Tag>::clear(rm,tx);
+ }
+ private:
+ transaction_id read_tid;
+ };
+
+
+ test_stm &rm;
+ transaction_id id_;
+ transaction *parent_;
+ bool active;
+ typedef boost::ptr_vector<access_base> accesses_type;
+ accesses_type accesses;
+};
+
+
+template<class T,class Tag>
+class test_stm::object{
+public:
+ static T read(test_stm const &rm){
+ iterator it=find(rm);
+ if(it == instances.end()) return T();
+ else return it->second.value;
+ }
+ static T read(test_stm const &rm,transaction &tx){
+ return read(find(rm,tx),tx);
+ }
+ static void write(test_stm const &rm,transaction &tx,T const &t){
+ iterator it=find(rm,tx);
+ if(it == instances.end() || it->first.second != tx.id()){ //instance not found or does not belong to tx
+ T value=read(it,tx);
+ std::pair<iterator,bool> ret=instances.insert(std::make_pair(std::make_pair(&rm,tx.id()),instance<T>(value,tx.id())));
+ BOOST_ASSERT(ret.second);
+ it=ret.first;
+ }
+ BOOST_ASSERT(it->first.second == tx.id());
+ it->second.value=t;
+ }
+ static void check(test_stm const &rm,transaction &tx,transaction_id read_tid){
+ iterator it=tx.parent() ? find(rm,*tx.parent()) : find(rm);
+ if(it != instances.end()){
+ if(it->second.last_update != read_tid){
+ throw boost::transact::resource_isolation_exception<test_stm>(rm,tx);
+ }
+ }else BOOST_ASSERT(read_tid == 0);
+ }
+ static void update(test_stm const &rm,transaction &tx){
+ iterator source=instances.find(std::make_pair(&rm,tx.id()));
+ if(source != instances.end()){
+ transaction_id destid=tx.parent() ? tx.parent()->id() : 0;
+ instances[std::make_pair(&rm,destid)]=source->second;
+ }
+ }
+ static void clear(test_stm const &rm,transaction &tx){
+ instances.erase(std::make_pair(&rm,tx.id()));
+ }
+private:
+ typedef std::map<instance_id,instance<T> > instances_type;
+ typedef typename instances_type::iterator iterator;
+
+ static T read(iterator it,transaction &tx){
+ if(it == instances.end()){
+ tx.register_access<T,Tag>(0);
+ return T();
+ }else{
+ if(it->first.second != tx.id()){ //don`t register reads of local copies
+ tx.register_access<T,Tag>(it->second.last_update);
+ }
+ return it->second.value;
+ }
+ }
+ static typename instances_type::iterator find(test_stm const &rm){
+ return instances.find(std::make_pair(&rm,0));
+ }
+ static typename instances_type::iterator find(test_stm const &rm,transaction &tx){
+ typename instances_type::iterator it=instances.find(std::make_pair(&rm,tx.id()));
+ if(it == instances.end()){
+ if(tx.parent()) return find(rm,*tx.parent());
+ else return find(rm);
+ }else return it;
+ }
+
+ static instances_type instances;
+};
+
+template<class T,class Tag>
+typename test_stm::object<T,Tag>::instances_type test_stm::object<T,Tag>::instances;
+
+boost::mutex test_stm::mutex;
+
+inline void test_stm::commit_transaction(transaction &tx){
+ tx.check_active();
+ boost::lock_guard<mutex_type> lock(mutex);
+ tx.check();
+ tx.update();
+}
+inline void test_stm::rollback_transaction(transaction &tx){
+ tx.check_active();
+ boost::lock_guard<mutex_type> lock(mutex);
+ tx.rollback();
+}
+
+template<class T,class Tag>
+inline T test_stm::read(transaction &tx){
+ tx.check_active();
+ boost::lock_guard<mutex_type> lock(mutex);
+ return object<T,Tag>::read(*this,tx);
+}
+template<class T,class Tag>
+inline void test_stm::write(transaction &tx,T const &t){
+ tx.check_active();
+ boost::lock_guard<mutex_type> lock(mutex);
+ object<T,Tag>::write(*this,tx,t);
+}
+
+
+#endif


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk