
----- Original Message ----- From: <strasser@uni-bremen.de> To: <boost@lists.boost.org> Sent: Friday, February 19, 2010 10:06 PM Subject: Re: [boost] [transact] transaction language macros (was: Re: [transact] code in sandbox)
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
int priority=0; begin_transaction{ //... }retry{ stm::set_priority(priority++); }end_retry;
inside the retry-clause the transaction object is destructed and no new one is constructed yet. STM could provide an API for setting the priority of the next transaction, using thread-specific storage to support this.
I would prefer the transaction be available on retry. Of courset, this need to have a restart function.
I think the only way to support this on the macro level is using a boost::optional to hold the transaction, which probably introduces runtime overhead. (and I think another problem like the one below regarding "___control=0")
* committing the transaction and rethrowing a specific exception:
begin_transaction{ try{ //... throw my_exc(); //... }catch(my_exc &){ commit(); throw; } }end_transaction;
it seems to me that this already is (unintentionally) supported. (using active_transaction().commit()).
the scope is exited with an exception, so it does not commit again. commit_on_destruction is nullified.
Oh I see, I miss that.
only if the user commits manually and does not throw an exception there is a problem, but I think we can leave this case undefined. just like calling transaction::commit() two times in a row.
I agree. the user shouldn't need to commit explicitly.
* rollback the transaction and ignore a specific exception:
begin_transaction{ try{ //... throw my_exc(); //... }catch(my_exc &){ rollback(); or set_rollback_only(); } }end_transaction;
hmm...why? can't you catch the exceptionb outside of the scope?
try{ begin_transaction{ throw my_exc(); }end_transaction; catch(my_exc &) { //ignore }
Yes this do the same. The documentation should warn about the preceding usage.
* Should the following compile?
begin_transaction statement1; statement1; end_transaction;
it currently does. if we want to adhere to the syntax of a language extension:
transaction /statement/
we could enforce this by wrapping the user code in
do //user code while(false);
which supports a single statement and compound statements, but not multiple statements. I'm in favor of this. I used something similar to enforce the semicolon after "end_transaction".
You can not add another loop otherwise the break/continue will not exit the transaction loop. if(false);else works as expected.
* Just a remark: the following case doesn't do what the user could expect as the user is unable to set the variable __control to 0. It
this only affects the case the user "break"s from the retry clause. see my other email.
begin_transaction{ try{ //... }catch(boost::transact::isolation_exception & ex){ // do something before retry } }end_transaction;
if we move the __control=0; from the catch(isolation_exception &) clause up to the catch(...) one, the above use case seems OK. neither in the case that the whole transaction scope is exited by an exception , nor in the case that the retry-loop is continued the value of __control matters. it's only set to 0 for the "break out of retry" case.
There is yet a drawback to your current implemntation: * when the commit done on the destructor of the commit_on_destruction variable fails, you will throw an exception on a destructor. If the destructor is called due to a thrown exception the program will terminate. So, ~commit_on_destruction() should not throw. If ~commit_on_destruction() don't throw the isolation_exception can not be throw in this case, so the retry is not activated. I have added a parameter throw_catched to commit_on_destruction(), so not only ~commit_on_destruction() don't throw but also notified if the exception has been catched. template<class TxMgr> struct commit_on_destruction{ explicit commit_on_destruction(basic_transaction<TxMgr> &tx , bool &catched) // (NEW) : tx(&tx), exception_catched(catched) {} // (NEW) ~commit_on_destruction(){ if(this->tx) { try {// (NEW) exception_catched=false;// (NEW) this->tx->commit(); } catch(...) {// (NEW) exception_catched=true;// (NEW) }// (NEW) } } void nullify(){ this->tx=0; } private: basic_transaction<TxMgr> *tx; bool& exception_catched;// (NEW) }; With this new parameter we can throw after the destructor is called. We need to add a new block, which will throw if the exception was catched. bool ___exception_catched =false; \ NEW { \ NEW boost::transact::basic_transaction<TXMGR> ___tx; \ boost::transact::detail::commit_on_destruction<TXMGR> ___commit(___tx,___exception_catched ); \ NEW // as before } \ NEW if (must_throw) {\ NEW throw boost::transact::isolation_exception(); \ NEW }\ NEW * return could not be transparent. The following code could return even if the transaction int f() { begin_transaction return variable; end_transaction; } Note that when we do a return in the transaction block, the lines if (must_throw) {\ NEW throw boost::transact::isolation_exception(); \ NEW }\ NEW will not be executed, only the destructor are executed. As ~commit_on_destruction() must catch the possible isolation exception, if the commit fails there will be no exception. So return will return from the function even if the commit fails. We need to commit explicitly before returning. int f() { begin_transaction RETURN(variable); end_transaction; } RETURN(var) must commit the transaction and return the parameter evaluated on the context of the transaction. But you need to commit using the commit_on_destruction variable, because otherwise you will commit twice. Boost.STM has added a commit function to the commit_on_destruction class void commit() { if (tx_!=0&&!commited_) { tx_->commit(); commited_=true; } } Boost.STM defines a macro #define BOOST_STM_RETURN(TX, EXPRESSION) \ return boost::stm::detail::commit_and_return(BOOST_STM_VAR_DESTR(TX), EXPRESSION) template <typename TX, typename T> T commit_and_return(TX &t, T const& var) { T tmp(var); t.commit(); return tmp; } You can inspire from this to solve the same issues on Boost.Transact. Please let me know if I missed something. I have resumed in the following pseudo code the code generated by my macros depending on whether * the current transaction block is in a loop IN_LOOP * there are specific exception hadlers HANDLER_SEQ * there is a specific retry (RETRY)
frame transaction_block = keyword<transaction> statement<BODY> [ handler_seq<HANDLER_SEQ> ] [ keyword<retry> compound_statement<RETRY> ] var IN_LOOP = ... current transaction block in a loop transaformation { bool __stop = false; if in_loop boost::stm::detail::control_flow __ctrl; endif do { boost::stm::transaction __TX; try{ bool __catched=false; { boost::stm::detail::commit_on_destruction destr(__TX, __stop, __catched); try{ if IN_LOOP do { BOOST_STM_VAR_CTRL(TX)=boost::stm::detail::break_; endif if present(HANDLER_SEQ) try { endif BODY present(HANDLER_SEQ) } HANDLER_SEQ endif
if IN_LOOP __ctrl=boost::stm::detail::none; } while ((BOOST_STM_VAR_CTRL(TX)=boost::stm::detail::continue_),false); endif destr.commit(); } catch(...) { destr.release(); throw; } } if (catched) { throw boost::stm::aborted_tx("commit throw"); } break; } catch (boost::stm::aborted_tx &) { if (TX.is_nested()) throw; TX.restart(); RETRY } } while(!stop);
if IN_LOOP if (ctrl==boost::stm::detail::continue_) continue; else if (BOOST_STM_VAR_CTRL(TX)==boost::stm::detail::break_) break; endif
}
end_frame
Afterwards I think that it will be very useful if we had a meta-language that allowing code transaformations. With a such meta-language we could just write transaction { ... } retry { ... } or transaction { ... } catch (Ex1& ex) { ... } retry { ... } Best, Vicente