Re: [boost] [transact] code in sandbox

----- Original Message ----- From: <strasser@uni-bremen.de> To: "vicente.botet" <vicente.botet@wanadoo.fr> Sent: Friday, February 12, 2010 2:00 AM Subject: Re: Re:[boost] [transact] code in sandbox
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
IMO the transaction must be accesible during the handling of the exceptions.
why? I see one reason in your examples (although I don't understand force_to_abort, maybe that's another one, see below), and that is committing on exception:
#define BOOST_TRANSACT_BASIC_RETHROW(TXMGR, TX, E) \\ catch (E&) {(TX).commit(); throw;}
why can't the user create his own try/catch block for that?: (using your syntax) BOOST_TRANSACT_ATOMIC(_) { try{ ... }catch(my_exc &){ _.commit(); } }BOOST_TRANSACT_COMMIT();
Yes this is always possible. However, transactions and exception handling are used for the same goal, recover from errors. Thus it is interesting to handle them in the same way and in a single try-cactch block.
_.force_to_abort();
what does this do? retry the transaction but make sure it fails? why? why don't you just go ahead and throw an exception that is not caught inside the transaction scope?
When the user wants to undo whatever the transacton block did, the simple way is to abort the transaction. Don't forget that transactions can be used in a single-threaded application to make exception-safe code.
at some point I think the macros loose their benefit and you might just as well write the loop yourself. e.g. if you need to execute custom code on retry.
You are right. The specific macros are not important. What is important is that the provided macros can be extended naturaly.
that's what I'm not so sure of. if you need to control the number of retries or execute custom code on retry, why don't you just write this instead of using macros:
for(int c=0;c<10;++c){ try{ transaction tx; ... tx.commit(); break; }catch(isolation_exceotion &e){ e.unwind(); std::cerr << "my custom retry code\n"; } }
doesn't seem a whole lot more verbose to me. but I'm not against adding more macros for convenience. the one thing I would like is not complicating the simplest use case. the user shouldn't have to to add something like "(_)" if he doesn't need it and has no idea what it is there for. the default use case should be as simple as possible.
I would like also to don't need to add a transaction variable name. I need the name of the transaction variables to implement break and continue in a language-like. From my side, the inclusion of the macros is to be close to what the user will write if the language provided some specific transaction constructs. The user is always free to use directly the class interface. The question is what is the simple case. for an INNER transaction Boost.STM provides a macro #define BOOST_STM_USE_ATOMIC(TX) \ for (boost::stm::transaction TX; \ ! TX.committed() \ && TX.restart(); \ TX.commit()) that the user ca use like BOOST_STM_USE_ATOMIC(_) { // transaction statement } In this case every exception scape the transaction block, and will be managed by the outer transaction block. This is simple, recall a possible language statement transaction { // transaction statements } The parameter is needed only if we want to return from the transaction block. If needed we can add a simple #define BOOST_STM_INNER_TRANSACTION \ for (boost::stm::transaction __TX; \ ! __TX.committed() \ && __TX.restart(); \ __TX.commit()) use as follows BOOST_STM_INNER_TRANSACTION { // transaction statement } As I said in a preceding post, we can provide initialy both sets of macros and see which set the users use more often. Best, Vicente

Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
why can't the user create his own try/catch block for that?: (using your syntax) BOOST_TRANSACT_ATOMIC(_) { try{ ... }catch(my_exc &){ _.commit(); } }BOOST_TRANSACT_COMMIT();
Yes this is always possible. However, transactions and exception handling are used for the same goal, recover from errors. Thus it is interesting to handle them in the same way and in a single try-cactch block.
why is this interesting? if your goal is a language-like syntax, the user should not even have to be aware that the macro is implemented using a try/catch block. so why should he be able to add a catch-block? what's the advantage? is this the only reason that you restart transaction objects instead of destructing and reconstructing them, or is there another? from my viewpoint, it doesn't make sense to "restart" a transaction. when a transaction fails, you can repeat the operations in it in a new transaction. but there is no such thing as "restarting" it. this would require considerable support from resource managers, so that each of them can reuse the transaction object for a new transaction.
_.force_to_abort();
what does this do? retry the transaction but make sure it fails? why? why don't you just go ahead and throw an exception that is not caught inside the transaction scope?
When the user wants to undo whatever the transacton block did, the simple way is to abort the transaction. Don't forget that transactions can be used in a single-threaded application to make exception-safe code.
yes, but what's wrong with throwing an exception that is uncaught in the transaction scope? undoing the transaction is the default behaviour when a transaction object is destructed.
The question is what is the simple case. for an INNER transaction Boost.STM provides a macro
snip
#define BOOST_STM_USE_ATOMIC(TX) \ for (boost::stm::transaction TX; \ ! TX.committed() \ && TX.restart(); \ TX.commit())
I was talking about "simple" from the user's perspective, not implementation-wise. I'd also prefer a macro xxx{} instead of a combination of two macros xxx{}yyy(); but since this is not possible in all cases, it is better to provide only the latter imho. providing another macro only to avoid writing yyy() in some cases(nested transactions) is confusing. you have to understand implementation details in order to decide if you can save yourself writing yyy().
As I said in a preceding post, we can provide initialy both sets of macros and see which set the users use more often.
yes, we can always do that. I'd still like to understand the benefit of the additional macros. you have clearly spent more time than me on this topic and I'm looking forward to the paper, but right now I honestly don't see the benefit. the benefit of the simple macro is 1) a less verbose interface, not having to write a loop each time, and 2) saving the user from the implementation details. when the user needs to have control over code-on-retry etc., using the macros is not less verbose and the user has to be aware of the details that this is implemented as a loop and a try/catch block. having more than one choice on which macro to use even adds complexity.

strasser wrote:
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
why can't the user create his own try/catch block for that?: (using your syntax) BOOST_TRANSACT_ATOMIC(_) { try{ ... }catch(my_exc &){ _.commit(); } }BOOST_TRANSACT_COMMIT();
Yes this is always possible. However, transactions and exception handling are used for the same goal, recover from errors. Thus it is interesting to handle them in the same way and in a single try-cactch block.
why is this interesting?
One try-catch block instead on two :)
if your goal is a language-like syntax, the user should not even have to be aware that the macro is implemented using a try/catch block. so why should he be able to add a catch-block? what's the advantage?
This is the reason to provide the macros BEFORE_RETRY, ON_EXCEPTION, and so on. To hide this to the user.
is this the only reason that you restart transaction objects instead of destructing and reconstructing them, or is there another?
The transaction variable must exist while handling the possible exceptions. As the transaction variable has not been destroyed yet, we need to restart the transaction on retry.
from my viewpoint, it doesn't make sense to "restart" a transaction. when a transaction fails, you can repeat the operations in it in a new transaction. but there is no such thing as "restarting" it.
this would require considerable support from resource managers, so that each of them can reuse the transaction object for a new transaction.
On TBoost.STM the restat operation is less expensive than creating a transaction.
_.force_to_abort();
what does this do? retry the transaction but make sure it fails? why? why don't you just go ahead and throw an exception that is not caught inside the transaction scope?
When the user wants to undo whatever the transacton block did, the simple way is to abort the transaction. Don't forget that transactions can be used in a single-threaded application to make exception-safe code.
yes, but what's wrong with throwing an exception that is uncaught in the transaction scope? undoing the transaction is the default behaviour when a transaction object is destructed.
Sometimes the user can not throw an exception because s/he is handling already an exception, on it is in a no throw function destructor, ... In these cases it will be more convenient to just state that the transaction could only abort.
The question is what is the simple case. for an INNER transaction Boost.STM provides a macro
snip
#define BOOST_STM_USE_ATOMIC(TX) \ for (boost::stm::transaction TX; \ ! TX.committed() \ && TX.restart(); \ TX.commit())
I was talking about "simple" from the user's perspective, not implementation-wise. I'd also prefer a macro xxx{} instead of a combination of two macros xxx{}yyy(); but since this is not possible in all cases, it is better to provide only the latter imho. providing another macro only to avoid writing yyy() in some cases(nested transactions) is confusing. you have to understand implementation details in order to decide if you can save yourself writing yyy().
This use is simple for the user perspective and efficient from the implementation point of view. I respect you wish to provide only a macro, but IMO there are more use cases than one on which macros could be used.
As I said in a preceding post, we can provide initially both sets of macros and see which set the users use more often.
yes, we can always do that. I'd still like to understand the benefit of the additional macros.
Well you see these as additional macros. I see as initial macros from TBoost.STM.
you have clearly spent more time than me on this topic and I'm looking forward to the paper, but right now I honestly don't see the benefit. the benefit of the simple macro is 1) a less verbose interface, not having to write a loop each time, and 2) saving the user from the implementation details.
With my macros or with yours, the user needs to be aware that the transaction block is included on a internal loop. Otherwise his code could not work. For example with your macro (that hides the transaction variable) the user is unable to exit successfully from the transaction block. int f() { BOOST_TRANSACT_ATOMIC { ... return 1; // do not commits ... }BOOST_TRANSACT_COMMIT; } will not behaves as the user expects. With the STM macros the user could do int f() { BOOST_TRANSACT_ATOMIC(_) { ... _.commit(); return 1; // or BOOST_TRANSACT_RETURN(_, 1); ... } BOOST_TRANSACT_RETRY(_); }
when the user needs to have control over code-on-retry etc., using the macros is not less verbose and the user has to be aware of the details that this is implemented as a loop and a try/catch block. having more than one choice on which macro to use even adds complexity.
Maybe you are right, may be not. All these depend on whether we found a good macro name for each common use case. I hope we will find out these common use cases and appropriated names together. Best, Vicente -- View this message in context: http://old.nabble.com/-transact--code-in-sandbox-tp27515535p27563130.html Sent from the Boost - Dev mailing list archive at Nabble.com.

Zitat von Vicente Botet Escriba <vicente.botet@wanadoo.fr>:
One try-catch block instead on two :)
has no advantage.
On TBoost.STM the restat operation is less expensive than creating a transaction.
do you wish to change/extend the ResourceManager concept because of this? in what way?
Sometimes the user can not throw an exception because s/he is handling already an exception, on it is in a no throw function destructor, ... In these cases it will be more convenient to just state that the transaction could only abort.
ok. what does the call to transaction::commit() then do? throw? which exception?
This use is simple for the user perspective and efficient from the implementation point of view. I respect you wish to provide only a macro, but IMO there are more use cases than one on which macros could be used.
ok, my main point is that TRANSACT_ATOMIC/TRANSACT_COMMIT should be not made more complicated in favor of more sophisticated use cases. if there is a seperate set of macros, that's fine. but if these should not be STM-specific but usable with Boost.Transact in general, please describe what changes to the TransactionManager/ResourceManager concepts and the basic_transaction class you require for implementing the macros. (one of which would be reusing transaction objects, as discussed above. what else?)
With my macros or with yours, the user needs to be aware that the transaction block is included on a internal loop.
not necessarily. when you "return" from a transaction scope, commit() isn't reached, so the transaction is not committed. in this case: do{ return 1; }while(a++ == 5); you don't expect that the side effects of the while-clause still takes effect. so I don't see a problem in the behaviour that "return" omits the commit either. you could of course offer a macro like COMMIT_AND_RETURN.

strasser wrote:
Zitat von Vicente Botet Escriba <vicente.botet@wanadoo.fr>:
With my macros or with yours, the user needs to be aware that the transaction block is included on a internal loop.
not necessarily. when you "return" from a transaction scope, commit() isn't reached, so the transaction is not committed.
in this case:
do{ return 1; }while(a++ == 5);
you don't expect that the side effects of the while-clause still takes effect. so I don't see a problem in the behaviour that "return" omits the commit either. you could of course offer a macro like COMMIT_AND_RETURN.
How the user can return successfully with your macros? Vicente -- View this message in context: http://old.nabble.com/-transact--code-in-sandbox-tp27515535p27564662.html Sent from the Boost - Dev mailing list archive at Nabble.com.

Zitat von Vicente Botet Escriba <vicente.botet@wanadoo.fr>:
do{ return 1; }while(a++ == 5);
you don't expect that the side effects of the while-clause still takes effect. so I don't see a problem in the behaviour that "return" omits the commit either. you could of course offer a macro like COMMIT_AND_RETURN.
How the user can return successfully with your macros?
you can't, the macros are supposed to be a simplified syntax for the most common use case, not replace using the 'transaction' class directly. if you want to provide a macro syntax that covers every use case you can think of I'm ok with that(although I'd still prefer using 'transaction' directly in those cases). just try not to confuse users who only want a simple syntax with stuff like (_), having to understand implementation details or having to choose from multiple macros based on where they are used (nested or not). I guess the only way to achieve that is 2 seperate sets of macros. let me know what kind of support from RMs/TMs/transactions you need for these macros.

----- Original Message ----- From: <strasser@uni-bremen.de> To: <boost@lists.boost.org> Sent: Friday, February 12, 2010 4:18 PM Subject: Re: [boost] [transact] code in sandbox
I guess the only way to achieve that is 2 seperate sets of macros.
let me know what kind of support from RMs/TMs/transactions you need for these macros.
Hi, here they are some of operations I need TM::transaction void commit(); void rollback(); | void abort(); void rollback_only(); | void force_to_abort(); void restart(); bool is_top_level(); transaction_state status(); where enum transaction_state { active, marked_roolback_only, | forced_to_abort prepared, committed, rolledback, preparing, committing, rollingback ... }; or bool active(); bool marked_roolback_only() bool prepared() bool commited(); ... Best, Vicente

it obviously makes sense to keep the requirements on RMs/TMs as simple as possible, so sorry if I seem to attack every single one of your requirements. I only do this to make sure we don't introduce unnecessary requirements on every RM. Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
void rollback_only(); | void force_to_abort();
what's the difference between calling rollback() and calling rollback_only? I guess rollback_only() doesn't rollback immediatly but only marks the transaction and doesn't let you commit afterwards. but what is the benefit of that? example? what does commit() do after rollback_only() was called? throw? can't you then call rollback() instead of rollback_only() and let commit() throw when the transaction is already rolled back?
void restart();
we've discussed this shortly before, making RMs support restarting transactions is not trivial. it would mean that unspecified begin_root_transaction(); is changed to void begin_root_transaction(transaction &); and "transaction" must be DefaultConstructible. and every RM must support cleaning up a "transaction" object, possibly using a pointer to an internal transaction object or using boost::optional's internally to be able to re-construct it, if not all objects that are internally used can be cleared. do you really think this is worth having the small performance gain you talked about, ONLY in the case of an isolation_exception? if you need this for the purpose of the macro syntax only, you could wrap basic_transaction in a simple class like this: struct restartable_transaction{ void restart(){ tx=in_place(); } optional<basic_transaction> tx; }; I'd really like to keep this out of the RM requirements.
bool is_top_level();
what's top-level? root?
transaction_state status(); where enum transaction_state { active, marked_roolback_only, | forced_to_abort prepared, committed, rolledback, preparing, committing, rollingback ... }; or bool active(); bool marked_roolback_only() bool prepared() bool commited();
some of the status here seem to be "unstable" to me, i.e. would never be in effect while the user has a chance to query it. committing a transaction is a non-thread-safe operation (i.e., you can't commit in one thread and query the status of the transaction in another thread concurrently). so unless I'm missing something most of the listed status are never in effect when the caller has control. e.g., when would prepared() return true? what's left is: active, committed, rolledback, and possibly marked_rollback_only.

have you come to a conclusion on the issue below? if you absolutely need this I'll implement it, probably as an optional Service a RM can provide, and when it doesn't the TM reconstructs the transaction on restart(). I'd still prefer not to though, as it complicates the TM and don't think there is much logic behind "restarting" a transaction. except reusing the resources occupied by it, but maybe that can be achieved in other ways internally by the RM. Zitat von strasser@uni-bremen.de:
void restart();
we've discussed this shortly before, making RMs support restarting transactions is not trivial. it would mean that
unspecified begin_root_transaction();
is changed to
void begin_root_transaction(transaction &);
and "transaction" must be DefaultConstructible.
and every RM must support cleaning up a "transaction" object, possibly using a pointer to an internal transaction object or using boost::optional's internally to be able to re-construct it, if not all objects that are internally used can be cleared.
do you really think this is worth having the small performance gain you talked about, ONLY in the case of an isolation_exception?
if you need this for the purpose of the macro syntax only, you could wrap basic_transaction in a simple class like this:
struct restartable_transaction{ void restart(){ tx=in_place(); } optional<basic_transaction> tx; };
I'd really like to keep this out of the RM requirements.

author="strasser"> have you come to a conclusion on the issue below? if you absolutely need this I'll implement it, probably as an optional Service a RM can provide, and when it doesn't the TM reconstructs the transaction on restart(). I'd still prefer not to though, as it complicates the TM and don't think there is much logic behind "restarting" a transaction. except reusing the resources occupied by it, but maybe that can be achieved in other ways internally by the RM. As we have seen off-list it seems that restart should not be needed. -- View this message in context: http://old.nabble.com/-transact--code-in-sandbox-tp27515535p27622248.html Sent from the Boost - Dev mailing list archive at Nabble.com.

On Sun, Feb 14, 2010 at 6:53 PM, vicente.botet <vicente.botet@wanadoo.fr> wrote:
Hi, here they are some of operations I need
void rollback(); | void abort(); void rollback_only(); | void force_to_abort();
Can you clarify the difference between these two?
void restart();
bool is_top_level(); transaction_state status(); where enum transaction_state { active, marked_roolback_only, | forced_to_abort prepared, committed, rolledback, preparing, committing, rollingback ... };
If you are expecting the TM used in common among all libraries to support these states, then you have to clarify what these states mean, and how they apply to the interaction between the TM and the RM of Boost.STM. Have you considered the possibility that some of these states might be needed only on the STM-specific transaction data, tracked interanlly. I am guessing that marked_rollback_only, preparing, committing, and rollingback might all be statuses which you don't have to expose in your interaction with the TM. Thanks, - Bob

----- Original Message ----- From: "Bob Walters" <bob.s.walters@gmail.com> To: <boost@lists.boost.org> Sent: Wednesday, February 17, 2010 7:43 AM Subject: Re: [boost] [transact] code in sandbox On Sun, Feb 14, 2010 at 6:53 PM, vicente.botet <vicente.botet@wanadoo.fr> wrote:
Hi, here they are some of operations I need
void rollback(); | void abort(); void rollback_only(); | void force_to_abort();
Can you clarify the difference between these two?
void restart();
bool is_top_level(); transaction_state status(); where enum transaction_state { active, marked_roolback_only, | forced_to_abort prepared, committed, rolledback, preparing, committing, rollingback ... };
If you are expecting the TM used in common among all libraries to support these states, then you have to clarify what these states mean, and how they apply to the interaction between the TM and the RM of Boost.STM. Have you considered the possibility that some of these states might be needed only on the STM-specific transaction data, tracked interanlly. I am guessing that marked_rollback_only, preparing, committing, and rollingback might all be statuses which you don't have to expose in your interaction with the TM. Thanks, - Bob _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost The "|" marks alternative names, i.e. two proposals. preparing, committing, and rollingback should not appear, as the comit is not done asynchronously as all the resource managers are local to the process. marked_rollback_only or forced_to_abort seems to me a unseful information for the user. Vicente

On Wed, Feb 17, 2010 at 2:52 AM, vicente.botet <vicente.botet@wanadoo.fr> wrote:
On Sun, Feb 14, 2010 at 6:53 PM, vicente.botet <vicente.botet@wanadoo.fr> wrote:
Hi, here they are some of operations I need
void rollback(); | void abort(); void rollback_only(); | void force_to_abort();
Can you clarify the difference between these two?
The "|" marks alternative names, i.e. two proposals.
I meant can you clarify the difference between rollback() and rollback_only().
marked_rollback_only or forced_to_abort seems to me a unseful information for the user.
That makes sense. I think I recall a concept like this in Boost.Persistent - this is for the case where the user marks a transaction as 'uncomittable' to make sure code elsewhere can only try to rollback, and never commit. (Correct?) I can agree to support this.

On Fri, Feb 12, 2010 at 7:54 AM, Vicente Botet Escriba <vicente.botet@wanadoo.fr> wrote:
With my macros or with yours, the user needs to be aware that the transaction block is included on a internal loop. Otherwise his code could not work. For example with your macro (that hides the transaction variable) the user is unable to exit successfully from the transaction block.
int f() { BOOST_TRANSACT_ATOMIC { ... return 1; // do not commits ... }BOOST_TRANSACT_COMMIT; }
will not behaves as the user expects. With the STM macros the user could do
int f() { BOOST_TRANSACT_ATOMIC(_) { ... _.commit(); return 1; // or BOOST_TRANSACT_RETURN(_, 1);
... } BOOST_TRANSACT_RETRY(_); }
Actually, looking back at the start of this whole thread, the original macros were 'do atomic{' and '}commit()' to specifically help show the do/while loop. And looking at the above, the RETRY macro at the end of f() doesn't do as effective a job conveying the presence of the loop. (IMHO.) You've made a good point here. The macros can hide too much from the end user. That is one of the things underlying my own dislike of macros. They save time writing code, but make it harder to read, and debug. The use of macros for these loops should be optional. For that matter, the loops are optional. In my library, a user can employ pessimistic locking conventions to ensure no chance of an isolation_exception, and in the process, would not need the retry loop.
when the user needs to have control over code-on-retry etc., using the macros is not less verbose and the user has to be aware of the details that this is implemented as a loop and a try/catch block. having more than one choice on which macro to use even adds complexity.
Maybe you are right, may be not. All these depend on whether we found a good macro name for each common use case. I hope we will find out these common use cases and appropriated names together.
I agree with Steffan on this point. Perhaps one way of approaching this, with a library like Boost.Transact is to have a clear, explicit model for dealing with a transaction, and provide some macros for the most common use case we can think of, and which we agree to be generic enough to apply to all 3 libraries. Additional macros, specific to capabilities that are only in one or two of the libraries could be provided with that library. For example, I do not currently have any capability to assign transaction priorities.

----- Original Message ----- From: "Bob Walters" <bob.s.walters@gmail.com> To: <boost@lists.boost.org> Sent: Wednesday, February 17, 2010 7:25 AM Subject: Re: [boost] [transact] code in sandbox On Fri, Feb 12, 2010 at 7:54 AM, Vicente Botet Escriba <vicente.botet@wanadoo.fr> wrote:
With my macros or with yours, the user needs to be aware that the transaction block is included on a internal loop. Otherwise his code could not work. For example with your macro (that hides the transaction variable) the user is unable to exit successfully from the transaction block.
int f() { BOOST_TRANSACT_ATOMIC { ... return 1; // do not commits ... }BOOST_TRANSACT_COMMIT; }
will not behaves as the user expects. With the STM macros the user could do
int f() { BOOST_TRANSACT_ATOMIC(_) { ... _.commit(); return 1; // or BOOST_TRANSACT_RETURN(_, 1);
... } BOOST_TRANSACT_RETRY(_); }
Actually, looking back at the start of this whole thread, the original macros were 'do atomic{' and '}commit()' to specifically help show the do/while loop. And looking at the above, the RETRY macro at the end of f() doesn't do as effective a job conveying the presence of the loop. (IMHO.) You've made a good point here. The macros can hide too much from the end user. That is one of the things underlying my own dislike of macros. They save time writing code, but make it harder to read, and debug. The use of macros for these loops should be optional. For that matter, the loops are optional. In my library, a user can employ pessimistic locking conventions to ensure no chance of an isolation_exception, and in the process, would not need the retry loop.
when the user needs to have control over code-on-retry etc., using the macros is not less verbose and the user has to be aware of the details that this is implemented as a loop and a try/catch block. having more than one choice on which macro to use even adds complexity.
Maybe you are right, may be not. All these depend on whether we found a good macro name for each common use case. I hope we will find out these common use cases and appropriated names together.
I agree with Steffan on this point. Perhaps one way of approaching this, with a library like Boost.Transact is to have a clear, explicit model for dealing with a transaction, and provide some macros for the most common use case we can think of, and which we agree to be generic enough to apply to all 3 libraries. Additional macros, specific to capabilities that are only in one or two of the libraries could be provided with that library. For example, I do not currently have any capability to assign transaction priorities. _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost Hi, the macros try to hide the complex code the user will need to hand write to get the same result. That means that if a loop is needed, the user will use a loop, and the problem will be on the side of the user. I think the library authors need to consider the situations the user will be in face of, to see if the provided interface is enough. The language-like macros, are just a mean to explore the domain. I don't think neither the criteria to don't accept capabilities that are only in one or two of the libraries. We need to explore what is useful to have and what is not useful independently from which library needs this now. The mix of optimistic and pessimistic strategies needs a careful design. On Boost.STM we have a LocakAware TM mechanism that could inspire us in this point. Whether the Transact library will take care of this requirement will depend on whether we found a good solution. We need to consider how contention management helps to avoid starvation with optimistic synchronization. Transaction priorities could be a way, other need to be considered, and see if the interface is enough open to let the user choose its contention management strategy. We are at the beginning, compiling requirements, evaluating different interfaces, etc, .. This would take some time and I'm sure that at the end we will reach an interface that satisfy the user and the author of the 3 libraries. I would prefer to left process management considerations out of this discussion and that we concentrate the discussion on concrete cases. Best regards, Vicente

Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
The mix of optimistic and pessimistic strategies needs a careful design.
could you explain what the difference is wrt Boost.Transact? whether there is an optimistic conflict or a pessimistic deadlock, the solution is retry.
We need to consider how contention management helps to avoid starvation with optimistic synchronization. Transaction priorities could be a way, other need to be considered, and see if the interface is enough open to let the user choose its contention management strategy.
could you point me to a good introduction on that issue? I always assumed low-contention and conflicts being the exception, which I think is a valid assumption for persistent objects, but not necessarily for transactional memory. in one example using your macros the priority of a transaction was increased on contention. I don't understand how that helps when two or more threads contend about a resource. the priority of all threads would increase.

strasser wrote:
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
The mix of optimistic and pessimistic strategies needs a careful design.
could you explain what the difference is wrt Boost.Transact? whether there is an optimistic conflict or a pessimistic deadlock, the solution is retry.
We need to consider how contention management helps to avoid starvation with optimistic synchronization. Transaction priorities could be a way, other need to be considered, and see if the interface is enough open to let the user choose its contention management strategy.
could you point me to a good introduction on that issue? I always assumed low-contention and conflicts being the exception, which I think is a valid assumption for persistent objects, but not necessarily for transactional memory. in one example using your macros the priority of a transaction was increased on contention. I don't understand how that helps when two or more threads contend about a resource. the priority of all threads would increase.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
You can start with Extending Contention Managers for User-Defined Priority Based Transactions from http://eces.colorado.edu/~gottschl/tboostSTM/pubs.html Let me know if you don't find the response there. Best, Vicente -- View this message in context: http://old.nabble.com/-transact--code-in-sandbox-tp27515535p27622298.html Sent from the Boost - Dev mailing list archive at Nabble.com.

I was thinking about making a bignum library for boost and wanted to gauge interest. I've read through the boost mailing list archives on this topic. I've seen mp_math in the vault by Kevin Sopp (AKA baraclese). I'm not sure if he's still working on it (he hasn't updated it in a while). What I want to do is similar. I made a bignum frontend with libtommath backend. This is a rough version of what I want to do. I thought I'd see if anyone was interested before I put more effort in to it. http://seth.dnsdojo.net/code/mpa.zip Note: This only compiles on Linux. I've read the boost library submission process guidelines and I will make this library conform to them if I continue to work on it. Note: "include/mpa/mpa.hpp" is the most interesting part. Look at "lib_src/libmpa/mpa.cpp" for the libtommath backend stuff. ----------------------------------- I think the project should have these specifications. Boost Library Name: MPA (Multi-Precision Arithmetic) Namespace: boost::mpa:: Guidelines: 1. Use PIMPL (to make different backends easier in the distant future). 2. DO NOT focus on backend work. Focus on making the frontend nice. Use libtommath backend (boost compatible license, portable, well tested, not as fast as GMP). 3. DO NOT focus on performance. Library won't get off the ground if it is required to be as fast as GMP from the start. 4. DO NOT implement floating point. Similar reason to #3. ----------------------------------- Is this worthwhile? Are any of the guidelines bad? -Seth

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Seth Bunce Sent: Friday, March 26, 2010 7:45 AM To: boost@lists.boost.org Subject: [boost] Bignum Library Interest?
I was thinking about making a bignum library for boost and wanted to gauge interest. I've read through the boost mailing list archives on this topic.
I've seen mp_math in the vault by Kevin Sopp (AKA baraclese). I'm not sure if he's still working on it (he hasn't updated it in a while). What I want to do is similar.
You should liaise with him?
I made a bignum frontend with libtommath backend. This is a rough version of what I want to do. I thought I'd see if anyone was interested before I put more effort in to it.
http://seth.dnsdojo.net/code/mpa.zip Note: This only compiles on Linux. I've read the boost library submission process guidelines and I will make this library conform to them if I continue to work on it. Note: "include/mpa/mpa.hpp" is the most interesting part. Look at "lib_src/libmpa/mpa.cpp" for the libtommath backend stuff.
Looks promising, but many believe that it is important to be able to switch to use GMP - if performance demands and license allows.
I think the project should have these specifications.
Boost Library Name: MPA (Multi-Precision Arithmetic) Namespace: boost::mpa::
Guidelines: 1. Use PIMPL (to make different backends easier in the distant future).
But see Barend Gehrels outline of a 'nuemric_adaptor' to make the switching work.
2. DO NOT focus on backend work. Focus on making the frontend nice. Use libtommath backend (boost compatible license, portable, well tested, not as fast as GMP). 3. DO NOT focus on performance. Library won't get off the ground if it is required to be as fast as GMP from the start. 4. DO NOT implement floating point. Similar reason to #3.
-----------------------------------
Are any of the guidelines bad?
These look OK to me. But would add - must be portable to multiplatform - must be Boost tests. - must be Boost licence throughout (you might simply get your 'other' author to agree to this?) Paul --- Paul A. Bristow Prizet Farmhouse Kendal, UK LA8 8AB +44 1539 561830, mobile +44 7714330204 pbristow@hetp.u-net.com

On Wed, Feb 17, 2010 at 3:27 AM, vicente.botet <vicente.botet@wanadoo.fr> wrote:
I don't think neither the criteria to don't accept capabilities that are only in one or two of the libraries. We need to explore what is useful to have and what is not useful independently from which library needs this now. We are at the beginning, compiling requirements, evaluating different interfaces, etc, .. This would take some time and I'm sure that at the end we will reach an interface that satisfy the user and the author of the 3 libraries. I would prefer to left process management considerations out of this discussion and that we concentrate the discussion on concrete cases.
My comment was not a "process management consideration". I'm attempting to understand the scope of Boost.Transaction. I was under the impression that it was to represent the intersection of the capabilities of the 3 libraries, not the union of those capabilities. I am also trying to understand the implications of this discussion thus far on the TM/RM interface. For example, I currently don't support transaction priorities. So I'm really just recommending that capabilities which are limited to one library not become part of the public API of Boost.transact, if instead there is an acceptable way to expose those controls directly via the library that offers that capability. For example, I am not going to suggest that there need to be Macros which designate timeout durations for the retry loop, or that the transaction class have a deadlock handling policy parameter because such things are not applicable to every RM which may run under the control of Boost.Transact.
The mix of optimistic and pessimistic strategies needs a careful design. On Boost.STM we have a LocakAware TM mechanism that could inspire us in this point. Whether the Transact library will take care of this requirement will depend on whether we found a good solution.
I would like to see your approach, and welcome the inspriation. I don't like what I currently have. My approach always involves the user explicitly designating when a pessimistic lock is to be acquired: const_iterator i = map->find(something); iterator<wait_policy> j = i( txn ); // something like this is needed to promote from const to non-const. int balance = j->second; // dereference j acquires pessimistic lock on that entry, and may block per wait_policy int new_balance = balance + 100; j->second = new_balance; // update entry as part of txn. This has generally made the combination of optimistic vs. pessimistic conventions possible within the scope of one transaction, but it does place the burden for doing it correctly on the user. The user could forget to lock the updated row correctly prior to updating it. One thing I can do in regard to that is add protections that ensure that either 1) the object is updated optimistically based on support for optimistic locking by the value_type of the map, or 2) the entry was locked correctly prior to the update. If the user violates both of those conditions, the update() throws an exception. However, there are use cases involving related entities where updating an unlocked row without protection is still safe, based on locks held elsewhere. So also need a convention for performing an unprotected update. And I'm currently convinced this whole policy of protection against improper locking needs to be optional, but available if users want the assurance it provides.
We need to consider how contention management helps to avoid starvation with optimistic synchronization. Transaction priorities could be a way, other need to be considered, and see if the interface is enough open to let the user choose its contention management strategy.
My approach to this is to allow pessimistic approaches as well as optimistic. i.e. optimistic works well for low contention. Under higher contention, the retry logic becomes a 'viscious cycle', warranting a pessimistic approach as the alternative. I have not thought about optimizing contention management in optimistic algorithms to any significant extent, I just accepted that it is not the perfect approach in all cases. I would agree that any pessimistic capability requires that the library support either deadlock prevention or deadlock detection (or both.) I haven't implemented deadlock detection, and am not looking forward to it, but agree it is the inevitable consequence of supporting the pessimistic approach. If you had better, optimistic-only alternatives, I would be very interested. Best Regards, Bob

Zitat von Bob Walters <bob.s.walters@gmail.com>:
The use of macros for these loops should be optional. For that matter, the loops are optional. In my library, a user can employ pessimistic locking conventions to ensure no chance of an isolation_exception, and in the process, would not need the retry loop.
how do you handle deadlocks? even pessimistic RDBMS require a retry-logic on the user's part in case of a deadlock, how do you avoid that?

On Wed, Feb 17, 2010 at 3:36 AM, <strasser@uni-bremen.de> wrote:
Zitat von Bob Walters <bob.s.walters@gmail.com>:
The use of macros for these loops should be optional. For that matter, the loops are optional. In my library, a user can employ pessimistic locking conventions to ensure no chance of an isolation_exception, and in the process, would not need the retry loop.
how do you handle deadlocks?
even pessimistic RDBMS require a retry-logic on the user's part in case of a deadlock, how do you avoid that?
Because I'm using entry-level locking, it is possible to write the algorithms so that all changes occurring in parallel threads happen in an agreed-upon order resulting in deadlock-free execution. For example, sorting all the keys of modified or deleted entries prior to performing those operations. I concede that in practice, most users won't do that, unless their algorithms are so contentious that the effort is warranted. So yes, I need deadlock handling, and for that, the standard retry loop / exception handling is appropriate.

Zitat von Bob Walters <bob.s.walters@gmail.com>:
when the user needs to have control over code-on-retry etc., using the macros is not less verbose and the user has to be aware of the details that this is implemented as a loop and a try/catch block. having more than one choice on which macro to use even adds complexity.
Maybe you are right, may be not. All these depend on whether we found a good macro name for each common use case. I hope we will find out these common use cases and appropriated names together.
I agree with Steffan on this point.
I don't agree with me anymore ;) I still do in principle, but the macro has grown a little more complex in our discussion, to support "break", "return" and "continue" inside of user loops. so when you're weighing using a macro against writing a loop yourself it is now more tilted towards using a macro, so maybe we should support something like BOOST_TRANSACT_ON_RETRY. control ctrl; while(true){ try{ transaction tx; commit_on_destruction destr(tx); try{ do{ ctrl=break_; //...user code if(something) break; else if(something_else) continue; else return 5; //...end user code ctrl=none; break; }while((ctrl=continue_),false); }catch(...){ destr.nullify(); throw; } break; }catch(isolation_exception &){} }; if(ctrl==continue_) continue; else if(ctrl==break_) break;

strasser@uni-bremen.de wrote: PMFJI...
control ctrl; while(true){ try{ transaction tx; commit_on_destruction destr(tx); try{ do{ ctrl=break_;
//...user code if(something) break; else if(something_else) continue; else return 5; //...end user code
ctrl=none; break; }while((ctrl=continue_),false); }catch(...){ destr.nullify(); throw; } break; }catch(isolation_exception &){} }; if(ctrl==continue_) continue; else if(ctrl==break_) break;
Couldn't you avoid the inner try block by detecting a pending exception in ~commit_on_destruction()? _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Zitat von "Stewart, Robert" <Robert.Stewart@sig.com>:
control ctrl; while(true){ try{ transaction tx; commit_on_destruction destr(tx); try{ do{ ctrl=break_;
//...user code if(something) break; else if(something_else) continue; else return 5; //...end user code
ctrl=none; break; }while((ctrl=continue_),false); }catch(...){ destr.nullify(); throw; } break; }catch(isolation_exception &){} }; if(ctrl==continue_) continue; else if(ctrl==break_) break;
Couldn't you avoid the inner try block by detecting a pending exception in ~commit_on_destruction()?
because of how std::uncaught_exception() works that would fail if the macros are used inside a destructor that is called during exception unwinding: http://www.gotw.ca/gotw/047.htm

strasser@uni-bremen.de wrote:
Zitat von "Stewart, Robert" <Robert.Stewart@sig.com>:
Couldn't you avoid the inner try block by detecting a pending exception in ~commit_on_destruction()?
because of how std::uncaught_exception() works that would fail if the macros are used inside a destructor that is called during exception unwinding:
I'm familiar with that issue, but I never imagined that would be a reasonable use case. Would you want to encourage using such code in a destructor by this design choice? _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Rob Stewart wrote:
strasser@uni-bremen.de wrote:
Zitat von "Stewart, Robert" <Robert.Stewart@sig.com>:
Couldn't you avoid the inner try block by detecting a pending exception in ~commit_on_destruction()?
because of how std::uncaught_exception() works that would fail if the macros are used inside a destructor that is called during exception unwinding:
I'm familiar with that issue, but I never imagined that would be a reasonable use case. Would you want to encourage using such code in a destructor by this design choice?
The question is not to encourage the use of such a code in a destructor. The code on the destructor can call a function that contains an inner transaction. Thus we have that this code can be executed on a destructor indirectly. Stephan, we shouldn't forget to add a test for this case. As we cannot throw an isolation_exception, we need to force the transaction to abort. Does this use case responds to the need for force_to_abort/set_rollback_only? Best, Vicente -- View this message in context: http://old.nabble.com/-transact--code-in-sandbox-tp27515535p27625796.html Sent from the Boost - Dev mailing list archive at Nabble.com.

Zitat von Vicente Botet Escriba <vicente.botet@wanadoo.fr>:
because of how std::uncaught_exception() works that would fail if the macros are used inside a destructor that is called during exception unwinding:
I'm familiar with that issue, but I never imagined that would be a reasonable use case. Would you want to encourage using such code in a destructor by this design choice?
I probably wouldn't encourage modifying persistent objects in destructors, but Boost.STM is intended as an alternative to manual thread synchronization. so if you've ever locked a mutex in a destructor, you know a use case for this.
As we cannot throw an isolation_exception, we need to force the transaction to abort. Does this use case responds to the need for force_to_abort/set_rollback_only?
I don't know, as I still don't understand rollback_only. I think Bob's question was about that too. not about the difference between force_to_abort/rollback_only, but the difference between rolling back and marking for rollback.

strasser wrote:
Zitat von Vicente Botet Escriba <vicente.botet@wanadoo.fr>:
because of how std::uncaught_exception() works that would fail if the macros are used inside a destructor that is called during exception unwinding:
I'm familiar with that issue, but I never imagined that would be a reasonable use case. Would you want to encourage using such code in a destructor by this design choice?
I probably wouldn't encourage modifying persistent objects in destructors, but Boost.STM is intended as an alternative to manual thread synchronization. so if you've ever locked a mutex in a destructor, you know a use case for this.
As we cannot throw an isolation_exception, we need to force the transaction to abort. Does this use case responds to the need for force_to_abort/set_rollback_only?
I don't know, as I still don't understand rollback_only. I think Bob's question was about that too. not about the difference between force_to_abort/rollback_only, but the difference between rolling back and marking for rollback.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
abort/rollback will abort the current transaction on the point of call, force_to_abort/set_rollback_only don' make the transaction to abort/rollback immediately, but tell to the the transaction that it must be aborted/rolledback next time we try to commit it. Vicente -- View this message in context: http://old.nabble.com/-transact--code-in-sandbox-tp27515535p27627621.html Sent from the Boost - Dev mailing list archive at Nabble.com.

Zitat von Vicente Botet Escriba <vicente.botet@wanadoo.fr>:
abort/rollback will abort the current transaction on the point of call, force_to_abort/set_rollback_only don' make the transaction to abort/rollback immediately, but tell to the the transaction that it must be aborted/rolledback next time we try to commit it.
some more details: 1) transaction tx; tx.rollback_only(); tx.commit(); //does what? throw? which exception? 2) transaction tx; tx.rollback_only(); pers_obj->value=1; //does what? //a) throw? which exception? //b) go ahead, only throw on commit. 3) transaction tx; tx.rollback(); tx.commit(); //throw? which exception? 4) transaction tx; tx.rollback(); pers_obj->value=1; //throw? which exception? please explain why for each case. current behaviour is the following, but subject to change: 1/2: non-existent 3: undefined behaviour(my RM throws no_active_transaction) 4: throw no_active_transaction()

----- Original Message ----- From: <strasser@uni-bremen.de> To: <boost@lists.boost.org> Sent: Wednesday, February 17, 2010 7:13 PM Subject: Re: [boost] [transact] code in sandbox
Zitat von Vicente Botet Escriba <vicente.botet@wanadoo.fr>:
abort/rollback will abort the current transaction on the point of call, force_to_abort/set_rollback_only don' make the transaction to abort/rollback immediately, but tell to the the transaction that it must be aborted/rolledback next time we try to commit it.
some more details:
1) transaction tx; tx.rollback_only(); tx.commit(); //does what? throw? which exception?
2) transaction tx; tx.rollback_only(); pers_obj->value=1; //does what? //a) throw? which exception? //b) go ahead, only throw on commit.
3) transaction tx; tx.rollback(); tx.commit(); //throw? which exception?
4) transaction tx; tx.rollback(); pers_obj->value=1; //throw? which exception?
please explain why for each case. current behaviour is the following, but subject to change: 1/2: non-existent 3: undefined behaviour(my RM throws no_active_transaction) 4: throw no_active_transaction()
I will add also 5) transaction tx; tx.commit(); tx.rollback(); 6) transaction tx; tx.commit(); pers_obj->value=1; Currently TBoost.STM throw aborted_transaction_exception in 1/2. 3/4/5/6 ignore the call as the transaction is not in the active state. A no_active_transaction would be even better. I will change this point. The use of macros should ensure that there is no operation out of protocol, so the cases 3/4/5/6 should never occur. Vicente

Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
1) transaction tx; tx.rollback_only(); tx.commit(); //does what? throw? which exception?
2) transaction tx; tx.rollback_only(); pers_obj->value=1; //does what? //a) throw? which exception? //b) go ahead, only throw on commit.
3) transaction tx; tx.rollback(); tx.commit(); //throw? which exception?
4) transaction tx; tx.rollback(); pers_obj->value=1; //throw? which exception?
please explain why for each case. current behaviour is the following, but subject to change: 1/2: non-existent 3: undefined behaviour(my RM throws no_active_transaction) 4: throw no_active_transaction()
I will add also 5) transaction tx; tx.commit(); tx.rollback();
6) transaction tx; tx.commit(); pers_obj->value=1;
Currently TBoost.STM throw aborted_transaction_exception in 1/2. 3/4/5/6 ignore the call as the transaction is not in the active state.
the reason for this list was my suspicion that rollback_only() isn't really necessary, and this confirms it. since 3-6 can be undefined, we can use rollback() instead of rollback_only(): transaction tx; tx.rollback(); tx.commit(); //throws no_active_transaction transaction tx; tx.rollback(); pers_obj->value=1; //throws no_active_transaction I would have understood if you wanted to support the following: A::~A(){ try{ pers_obj->value=1; }catch(isolation_exception &e){ transaction_manager::active_transaction().rollback_only(); }catch(...){ transaction_manager::active_transaction().rollback(); } } a transaction marked rollback would then throw isolation_exception on following accesses, and an actually rolled back transaction would throw no_active_transaction. was that your intention? if so, I still think we don't need rollback_only() if we define that a transaction that once emitted a isolation_exception has to emit it over and over again on following accesses and commit()s. a destructors can then simply ignore isolation_exceptions, as they will be rethrown when the caller tries to commit the transaction.

here's my implementation of the macros for discussion: https://svn.boost.org/trac/boost/changeset/59760 below are some example use cases. the (unachievable) ideal syntax would be transaction{ //... } for each case. actual syntax: begin_transaction{ //... }end_transaction; in user loops with support for control statements: for(...){ begin_transaction{ if(something) break; }end_transaction_in_loop; } the non-loop variant can be used in user loops if there are no control statements used. user code on retry, for example, limiting the number of retries to 5: int retries=0; begin_transaction{ //... }retry{ if(retries++ == 5) break; }end_retry; the same in a user loop: for(...){ begin_transaction{ if(something) break; }retry{ ... }end_retry_in_loop; } the same can be used for the (RM-specific) case we've discussed before to increase transaction priority on retry: int priority=0; begin_transaction{ stm::set_priority(priority); //... }retry{ ++priority; }end_retry; stm::set_priority() can use transaction_manager::active_transaction() to obtain the transaction. rolling back a transaction on exception: try{ begin_transaction{ //... throw my_exc(); //... }end_transaction; }catch(my_exc &){ //transaction is rolled back } committing the transaction in case of a specific exception: begin_transaction{ try{ //... throw my_exc(); //... }catch(my_exc &){ //tx will commit if my_exc is not rethrown } }end_transaction; invalid use cases: using in_loop outside of user loops: begin_transaction{ }end_transaction_in_loop; //compiler error: illegal "break" using control statements without in_loop: for(...){ begin_transaction{ if(something) break; }end_transaction; //error } this raises a runtime assertion if something==true. ideal would be a compiler error, but I don't think that is possible. are there any additional use cases to consider? comments on syntax? regards, Stefan

another comment on this case:
for(...){ begin_transaction{ if(something) break; //(1) }retry{ if(stop_trying) break; //(2) }end_retry_in_loop; }
it might be surprising to the user that (1) breaks the user loop, but (2) breaks the loop retrying the transaction. we could make (2) break the user loop with the same technique as is used for (1), but we need some way for the user to express he'd like to stop retrying the transaction, don't we?

Hi Stefan, ----- Original Message ----- From: <strasser@uni-bremen.de> To: <boost@lists.boost.org> Sent: Friday, February 19, 2010 4:07 PM Subject: [boost] [transact] transaction language macros (was: Re: [transact] code in sandbox)
here's my implementation of the macros for discussion:
Here's the mine for TBoost.STM http://svn.boost.org/svn/boost/sandbox/stm/branches/vbe/boost/stm/language_l... The main differences is that the name of the transaction variable is explicit and that I have added one more try-catch level I have suggested off list.
below are some example use cases. the (unachievable) ideal syntax would be
transaction{ //... }
for each case. actual syntax:
begin_transaction{ //... }end_transaction;
in user loops with support for control statements:
for(...){ begin_transaction{ if(something) break; }end_transaction_in_loop; }
the non-loop variant can be used in user loops if there are no control statements used.
user code on retry, for example, limiting the number of retries to 5:
int retries=0; begin_transaction{ //... }retry{ if(retries++ == 5) break; }end_retry;
the same in a user loop:
for(...){ begin_transaction{ if(something) break; }retry{ ... }end_retry_in_loop; }
the same can be used for the (RM-specific) case we've discussed before to increase transaction priority on retry:
int priority=0; begin_transaction{ stm::set_priority(priority); //... }retry{ ++priority; }end_retry;
stm::set_priority() can use transaction_manager::active_transaction() to obtain the transaction.
I would prefer int priority=0; begin_transaction{ //... }retry{ stm::set_priority(priority++); }end_retry;
rolling back a transaction on exception:
try{ begin_transaction{ //... throw my_exc(); //... }end_transaction; }catch(my_exc &){ //transaction is rolled back }
committing the transaction in case of a specific exception:
begin_transaction{ try{ //... throw my_exc(); //... }catch(my_exc &){ //tx will commit if my_exc is not rethrown } }end_transaction;
invalid use cases:
using in_loop outside of user loops:
begin_transaction{
}end_transaction_in_loop; //compiler error: illegal "break"
using control statements without in_loop:
for(...){ begin_transaction{ if(something) break; }end_transaction; //error }
this raises a runtime assertion if something==true. ideal would be a compiler error, but I don't think that is possible.
I like the assertion.
are there any additional use cases to consider?
* committing the transaction and rethrowing a specific exception: begin_transaction{ try{ //... throw my_exc(); //... }catch(my_exc &){ commit(); throw; } }end_transaction; This needs to use the transaction_manager::active_transaction() but will not change the state of commit_on_destruction variable. So we will commit twice. BTW, what is the result of? tx.commit(); tx.commit(); * rollback the transaction and ignore a specific exception: begin_transaction{ try{ //... throw my_exc(); //... }catch(my_exc &){ rollback(); or set_rollback_only(); } }end_transaction; This needs to use the transaction_manager::active_transaction() but will not change the state of commit_on_destruction variable. So we will commit after rollback. BTW, what is the result of? tx.rollback(); tx.commit(); * Should the following compile? begin_transaction statement1; statement1; end_transaction; That is, are the enclosing braces needed? * 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 should be stated clearly on the documentation that the user needs to use consistently the macros and that mixing macros and the class level functions is risky. begin_transaction{ try{ //... }catch(boost::transact::isolation_exception & ex){ // do something before retry } }end_transaction; for(;;) begin_transaction{ try{ //... }catch(boost::transact::isolation_exception & ex){ // do something before retry } }end_transaction_in_loop; }
comments on syntax?
As any macro in Boost.Transact must be prefixed with BOOST_TRANSACT, maybe BOOST_TRANSACT_TRANSACTION/BOOST_TRANSACT_END could be more appropiated than BOOST_TRANSACT_BEGIN_TRANSACTION/BOOST_TRANSACT_END_TRANSACTION BOOST_TRANSACT_TRANSACTION { //... } BOOST_TRANSACT_END ; BOOST_TRANSACT_TRANSACTION { //... } BOOST_TRANSACT_RETRY { //... } BOOST_TRANSACT_END_RETRY; We could add a 3rd level of try-catch around the user code, so the user can do BOOST_TRANSACT_TRANSACTION { //... } BOOST_TRANSACT_ON_EXCEPTION(Ex1) { ... } BOOST_TRANSACT_ON_EXCEPTION(Ex2){ BOOST_STM_COMMIT_AND_RETHROW(); }BOOST_TRANSACT_ON_EXCEPTION(Ex2){ BOOST_STM_ABORT(); // no rethrow }BOOST_TRANSACT_END ; Best, Vicente

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 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. 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.
* 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 }
* 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".
* 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.
As any macro in Boost.Transact must be prefixed with BOOST_TRANSACT, maybe BOOST_TRANSACT_TRANSACTION/BOOST_TRANSACT_END could be more appropiated than BOOST_TRANSACT_BEGIN_TRANSACTION/BOOST_TRANSACT_END_TRANSACTION
I thought about that, but since there are now multiple macros I also wanted to #define the lower case macros without prefix, if the user chooses to include them. I'd find it slightly confusing that end_transaction is defined to BASIC_TRANSACT_END when "BASIC_TRANSACT_" is kind of the namespace of the macro. see https://svn.boost.org/trac/boost/browser/sandbox/transaction/boost/transact/...
We could add a 3rd level of try-catch around the user code, so the user can do
BOOST_TRANSACT_TRANSACTION { //... } BOOST_TRANSACT_ON_EXCEPTION(Ex1) {
I still don't get it ;)

2 corrections:
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.
what I wrote here is nonsense. if the user "break"s in a self-defined catch(isolation_exception &) clause like you've shown above, the user's loop is affected. the value of __control is set correctly. if the user rethrows, the value doesn't matter.
As any macro in Boost.Transact must be prefixed with BOOST_TRANSACT, maybe BOOST_TRANSACT_TRANSACTION/BOOST_TRANSACT_END could be more appropiated than BOOST_TRANSACT_BEGIN_TRANSACTION/BOOST_TRANSACT_END_TRANSACTION
I thought about that, but since there are now multiple macros I also wanted to #define the lower case macros without prefix, if the user chooses to include them. I'd find it slightly confusing that
end_transaction
is defined to
BASIC_TRANSACT_END
when "BASIC_TRANSACT_" is kind of the namespace of the macro.
...unless we wanted to make the keywords "begin", "retry" and "end". I'm in favor if simple keywords, C++'s "try" and "catch" aren't very specific either. but unfortunately "begin" and "end" are used in STL containers.

----- 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

Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
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.
to me this seems similar to if(int v=f()){ //... }else{ //v is not in available here. } since the retry{} clause is syntactically a new scope.
* 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
if(false);else works as expected.
ok, will add that.
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.
this can not happen. see the call to nullify(). the whole lifetime of commit_on_destruction is wrapped in a try{} clause. if it throws, commit_on_destruction is nullified. so ~commit_on_destruction does not throw if the scope is exited by exception, and I see no problem with throwing from a destructor otherwise, not even in the case the macros are used in user's destructors. am I missing 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)
I don't quite understand what you're trying to say in this paragraph and the following pseudo code. defining a grammar for language extension/code transformation, or anything directly related to the macros? could you please comment on the following case, as I'm not sure what the solution is: for(...){ begin_transaction{ if(something) break; //(1) }retry{ if(stop_trying) break; //(2) }end_retry_in_loop; } possibilities: a) keep the current behaviour, (2) does not break the user loop, but the internal one b) make (2) break the user loop c) prohibit using control statements in retry{} clauses I tend to b). this would mean that the user has no easy way to stop retrying a transaction, but has to use c++ flow control for that, like exceptions or an own loop he can "break". but a) and c) would be pretty surprising to the user. e.g., retrying max. 5 times: try{ begin_transaction{ //... }retry{ if(retries++ == 5) throw my_exc(); }end_retry; }catch(my_exc &e){ //tx failed } on a related note, I think it should not be possible to rethrow from a retry{} clause: begin_transaction{ //... }retry{ throw; //error }; the fact that an isolation_exception was caught is an implementation detail, so we should move the user's code on retry out of the catch{} block. (which is not a problem since the "break" above skips the part below of it anyway) best,

----- Original Message ----- From: <strasser@uni-bremen.de> To: <boost@lists.boost.org> Sent: Sunday, February 21, 2010 5:37 PM Subject: Re: [boost] [transact] transaction language macros (was: Re: [transact] code in sandbox)
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
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.
to me this seems similar to
if(int v=f()){ //... }else{ //v is not in available here. }
since the retry{} clause is syntactically a new scope.
You are wrong here. The variable v is accesible on the else part.
* 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
if(false);else works as expected.
ok, will add that.
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.
this can not happen. see the call to nullify(). the whole lifetime of commit_on_destruction is wrapped in a try{} clause. if it throws, commit_on_destruction is nullified. so ~commit_on_destruction does not throw if the scope is exited by exception, and I see no problem with throwing from a destructor otherwise, not even in the case the macros are used in user's destructors. am I missing something?
Yes. In your code the try wrapping the commit_on_destruction variable doesn't nullify the variable as it is out of scope. See (1) and (2) #define BOOST_TRANSACT_BASIC_BEGIN_TRANSACTION(TXMGR) \ { \ int ___control; \ while(true){ \ try{ \ (1) boost::transact::basic_transaction<TXMGR> ___tx; \ boost::transact::detail::commit_on_destruction<TXMGR> ___commit(___tx); \ try{ \ (2) do{ \ ___control=1; #define BOOST_TRANSACT_BASIC_RETRY(TXMGR) \ ___control=0; \ break; \ }while((___control=2),false); \ }catch(...){ \ (2) ___commit.nullify(); \ throw; \ } \ break; \ }catch(boost::transact::isolation_exception &___i){ \ (2) ___i.unwind<TXMGR>(); \ ___control=0;
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)
I don't quite understand what you're trying to say in this paragraph and the following pseudo code. defining a grammar for language extension/code transformation, or anything directly related to the macros?
I find quite clear the pseudo-code . I was just saying that it would be great to have something like that in C++. Templates work with Types, variables and constants, but don't allows to pass code as parameters. Yes the idead will be to define a grammar and the transformation inside the C++ code.
could you please comment on the following case, as I'm not sure what the solution is:
for(...){ begin_transaction{ if(something) break; //(1) }retry{ if(stop_trying) break; //(2) }end_retry_in_loop; }
possibilities: a) keep the current behaviour, (2) does not break the user loop, but the internal one b) make (2) break the user loop c) prohibit using control statements in retry{} clauses
I tend to b). this would mean that the user has no easy way to stop retrying a transaction, but has to use c++ flow control for that, like exceptions or an own loop he can "break". but a) and c) would be pretty surprising to the user.
Option a) has no sens, has the user is not aware of an internal loop. There is a difference between breaking in the transaction block and breaking on the retry block. Breaking the loop on the the transaction block is expected to succeed the transaction. The retry block is here to repare the fact the transaction is isolated. If the user doesn't reach to repare maybe the solution is to throw the isolation_exception. Maybe asserting that there are no break/continue will be a good thing.
e.g., retrying max. 5 times:
try{ begin_transaction{ //... }retry{ if(retries++ == 5) throw my_exc(); }end_retry; }catch(my_exc &e){ //tx failed }
on a related note, I think it should not be possible to rethrow from a retry{} clause:
Why?
begin_transaction{ //... }retry{ throw; //error };
I would like the following: begin_transaction{ //... }retry{ if(retries++ == 5) throw; }end_retry;
the fact that an isolation_exception was caught is an implementation detail, so we should move the user's code on retry out of the catch{} block. (which is not a problem since the "break" above skips the part below of it anyway)
In the preceding code the user is not aware of a specific isolation_exception, but of one exception. We can state that when the user is on the retry block there is an exception that allows her/him to exit from the retry by rethrowing this exception. Best, Vicente

Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
I would prefer the transaction be available on retry. Of courset, this need to have a restart function.
to me this seems similar to
if(int v=f()){ //... }else{ //v is not in available here. }
since the retry{} clause is syntactically a new scope.
You are wrong here. The variable v is accesible on the else part.
oh. I guess that also means that if "v" has a destructor it is called at the end of the else-clause. so if this is a correct parallel the transaction should indeed be destructed at the end of the retry-clause. we can achieve that by moving the declaration of basic_transaction one line to the top, above "try{". but this still doesn't mean that stm::set_priority has anything to work with in the retry{} clause. (other than setting the priority of a transaction that is destructed shortly thereafter)
this can not happen. see the call to nullify(). the whole lifetime of commit_on_destruction is wrapped in a try{} clause. if it throws, commit_on_destruction is nullified. so ~commit_on_destruction does not throw if the scope is exited by exception, and I see no problem with throwing from a destructor otherwise, not even in the case the macros are used in user's destructors. am I missing something?
Yes. In your code the try wrapping the commit_on_destruction variable doesn't nullify the variable as it is out of scope. See (1) and (2)
could you elaborate? I don't see anything wrong. commit_on_destruction::~commit_on_destruction can throw, but ONLY if the user did not. so how can it happen that an exception is thrown during exception unwinding that leads to abort()?
#define BOOST_TRANSACT_BASIC_BEGIN_TRANSACTION(TXMGR) \ { \ int ___control; \ while(true){ \ try{ \ (1) boost::transact::basic_transaction<TXMGR> ___tx; \ boost::transact::detail::commit_on_destruction<TXMGR> ___commit(___tx); \ try{ \ (2) do{ \ ___control=1;
#define BOOST_TRANSACT_BASIC_RETRY(TXMGR) \ ___control=0; \ break; \ }while((___control=2),false); \ }catch(...){ \ (2) ___commit.nullify(); \ throw; \ } \ break; \ }catch(boost::transact::isolation_exception &___i){ \ (2) ___i.unwind<TXMGR>(); \ ___control=0;
I find quite clear the pseudo-code . I was just saying that it would be great to have something like that in C++. Templates work with Types, variables and constants, but don't allows to pass code as parameters.
I looked up c++0x lambda expressions a while ago, which are supposed to allow passing code, but I'd still prefer macros. with c++0x lambdas it would look something like transaction([](){ //user code },[](){ //code on retry });
Option a) has no sens, has the user is not aware of an internal loop.
There is a difference between breaking in the transaction block and breaking on the retry block. Breaking the loop on the the transaction block is expected to succeed the transaction. The retry block is here to repare the fact the transaction is isolated. If the user doesn't reach to repare maybe the solution is to throw the isolation_exception.
Maybe asserting that there are no break/continue will be a good thing.
I agree in principle, it doesn't make much sense to break from there. why would the user want to exit the scope in the same path as if the transactino succeeded. but I think, if he does, breaking from the user loop is expected behaviour. take e.g. a try catch block: for(...){ try{ //... }catch(...){ break; } } does the "break" make sense? hardly. why would you continue with the application as if the whole loop succeeded. but you still expect it to work. I'm not trying to encourage use of "break" there, but if you use it I think you'd expect it to work, not raise an assertion. agreed?
on a related note, I think it should not be possible to rethrow from a retry{} clause:
Why?
because the user isn't even aware that an exception was thrown. from his viewpoint, we could have implemented the whole thing with if(!tx.commit()){ //user's retry code }
I would like the following:
begin_transaction{ //... }retry{ if(retries++ == 5) throw; }end_retry;
what's wrong with explicitely stating the type of exception he wants to throw, instead of rethrowing an exception, even though there is no "catch" clause visible?

----- Original Message ----- From: <strasser@uni-bremen.de> To: <boost@lists.boost.org> Sent: Sunday, February 21, 2010 8:34 PM Subject: Re: [boost] [transact] transaction language macros (was: Re: [transact] code in sandbox)
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
I would prefer the transaction be available on retry. Of courset, this need to have a restart function.
to me this seems similar to
if(int v=f()){ //... }else{ //v is not in available here. }
since the retry{} clause is syntactically a new scope.
You are wrong here. The variable v is accesible on the else part.
oh. I guess that also means that if "v" has a destructor it is called at the end of the else-clause. so if this is a correct parallel the transaction should indeed be destructed at the end of the retry-clause. we can achieve that by moving the declaration of basic_transaction one line to the top, above "try{". but this still doesn't mean that stm::set_priority has anything to work with in the retry{} clause. (other than setting the priority of a transaction that is destructed shortly thereafter)
I agree this doesn't means that the transaction can not be removed shortly thereafter, but neither the oposite, that is that the transaction will be destructed shortly thereafter. The transaction could be reused if the implementation is improved. I have already, as I showed in a previous mail, moved the transaction declaration out of the try, so the transaction out leaves the retry, and the internal loop. { bool __stop = false; boost::stm::detail::control_flow __ctrl; do { *** boost::stm::transaction __TX; try{ bool __catched=false;
this can not happen. see the call to nullify(). the whole lifetime of commit_on_destruction is wrapped in a try{} clause. if it throws, commit_on_destruction is nullified. so ~commit_on_destruction does not throw if the scope is exited by exception, and I see no problem with throwing from a destructor otherwise, not even in the case the macros are used in user's destructors. am I missing something?
Yes. In your code the try wrapping the commit_on_destruction variable doesn't nullify the variable as it is out of scope. See (1) and (2)
could you elaborate? I don't see anything wrong. commit_on_destruction::~commit_on_destruction can throw, but ONLY if the user did not. so how can it happen that an exception is thrown during exception unwinding that leads to abort()?
You are right, the ~commit_on_destruction doesn't commits when called during exception unwinding as the catch nullify the commit_on_destruction variable. :(
I find quite clear the pseudo-code . I was just saying that it would be great to have something like that in C++. Templates work with Types, variables and constants, but don't allows to pass code as parameters.
I looked up c++0x lambda expressions a while ago, which are supposed to allow passing code, but I'd still prefer macros. with c++0x lambdas it would look something like
transaction([](){ //user code },[](){ //code on retry });
lambda expressions have function scope, so can not be used if the inner code use cotrol flow statement out of like break, continuer, goto ot return.
Option a) has no sens, has the user is not aware of an internal loop.
There is a difference between breaking in the transaction block and breaking on the retry block. Breaking the loop on the the transaction block is expected to succeed the transaction. The retry block is here to repare the fact the transaction is isolated. If the user doesn't reach to repare maybe the solution is to throw the isolation_exception.
Maybe asserting that there are no break/continue will be a good thing.
I agree in principle, it doesn't make much sense to break from there. why would the user want to exit the scope in the same path as if the transactino succeeded.
but I think, if he does, breaking from the user loop is expected behaviour. take e.g. a try catch block:
for(...){ try{ //... }catch(...){ break; } }
does the "break" make sense? hardly. why would you continue with the application as if the whole loop succeeded. but you still expect it to work. I'm not trying to encourage use of "break" there, but if you use it I think you'd expect it to work, not raise an assertion. agreed?
As I said already
Maybe asserting that there are no break/continue will be a good thing. That is, I prefer option c)
on a related note, I think it should not be possible to rethrow from a retry{} clause:
Why?
because the user isn't even aware that an exception was thrown. from his viewpoint, we could have implemented the whole thing with
if(!tx.commit()){ //user's retry code }
I would like the following:
begin_transaction{ //... }retry{ if(retries++ == 5) throw; }end_retry;
what's wrong with explicitely stating the type of exception he wants to throw, instead of rethrowing an exception, even though there is no "catch" clause visible?
If the user throws its own exception , s/he will need to catch it and do a specific action. If the exception is an isolation_exception, this exception will be catched by the outer transaction implicitly. As I have said
We can state that when the user is on the retry block there is an exception that allows her/him to exit from the retry by rethrowing this exception. At the end we are hanling an exceptional case, the transaction has been aborted. I find the best way to notify exceptional cases in C++ is a exception, so I don't think the user will be surprised by this fact. Are you?
For me retry is synomym of catch the transaction has been aborted. If we can't agree on this, I would like that the library provides a macro to abort completly the current transaction. Best, Vicente

Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
I agree this doesn't means that the transaction can not be removed shortly thereafter, but neither the oposite, that is that the transaction will be destructed shortly thereafter. The transaction could be reused if the implementation is improved. I have already, as I showed in a previous mail, moved the transaction declaration out of the try, so the transaction out leaves the retry, and the internal loop.
as I've said before, although I don't see the need for restarting a transaction,if you do, I'll implement it. it's not a big deal to implement, I only want to make sure to keep the RM interface as simple as possible. but if it's necessary for stuff like set_priority, not a problem. interface: basic_transaction::restart(); RM::restart_transaction(transaction &) if RM::services contains transaction_restart_service_tag; TM::restart_transaction(transaction &), calls RM::restart_transaction if available. reconstructs the transaction otherwise. ok?
As I said already
Maybe asserting that there are no break/continue will be a good thing. That is, I prefer option c)
ok, open issue then. but how does this relate to the fact that you see retry as an equivalent to catch, as you've said below? you can "break" from catch-clauses.
what's wrong with explicitely stating the type of exception he wants to throw, instead of rethrowing an exception, even though there is no "catch" clause visible?
If the user throws its own exception , s/he will need to catch it and do a specific action. If the exception is an isolation_exception, this exception will be catched by the outer transaction implicitly.
ok, then it's a matter of documentation, that "throw" not only rethrows exceptions but also "retries". but please consider this case first: try{ //... }catch(my_exc &){ transaction{ //... }retry{ throw; } } is it expected behaviour that this rethrows isolation_exception, not my_exc?

----- Original Message ----- From: <strasser@uni-bremen.de> To: <boost@lists.boost.org> Sent: Sunday, February 21, 2010 10:19 PM Subject: Re: [boost] [transact] transaction language macros (was: Re: [transact] code in sandbox)
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
I agree this doesn't means that the transaction can not be removed shortly thereafter, but neither the oposite, that is that the transaction will be destructed shortly thereafter. The transaction could be reused if the implementation is improved. I have already, as I showed in a previous mail, moved the transaction declaration out of the try, so the transaction out leaves the retry, and the internal loop.
as I've said before, although I don't see the need for restarting a transaction,if you do, I'll implement it.
it's not a big deal to implement, I only want to make sure to keep the RM interface as simple as possible. but if it's necessary for stuff like set_priority, not a problem.
interface: basic_transaction::restart(); RM::restart_transaction(transaction &) if RM::services contains transaction_restart_service_tag; TM::restart_transaction(transaction &), calls RM::restart_transaction if available. reconstructs the transaction otherwise. ok?
I'm not forcing you to do that. I 'm just saying both approaches can be explored. I prefer you explore without restart, and I continue to explore with restart. We will see which approach is better when we will complete the round of features.
As I said already
Maybe asserting that there are no break/continue will be a good thing. That is, I prefer option c)
ok, open issue then. but how does this relate to the fact that you see retry as an equivalent to catch, as you've said below? you can "break" from catch-clauses.
Yes, we can. But the result of the break is that we don't retry, i.e. we abort the transaction. If you prefer you can throw an isolation_exception when the user uses break or continue instead of asserting it don't use it.
what's wrong with explicitely stating the type of exception he wants to throw, instead of rethrowing an exception, even though there is no "catch" clause visible?
If the user throws its own exception , s/he will need to catch it and do a specific action. If the exception is an isolation_exception, this exception will be catched by the outer transaction implicitly.
ok, then it's a matter of documentation, that "throw" not only rethrows exceptions but also "retries". but please consider this case first:
try{ //... }catch(my_exc &){ transaction{ //... }retry{ throw; } }
is it expected behaviour that this rethrows isolation_exception, not my_exc?
yes, IMO the exception must be isolation_exception, or a specific exception the library documents and make the outer transaction to abort. Regards, Vicente

the changes we've discussed: https://svn.boost.org/trac/boost/changeset/59827 Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
Yes, we can. But the result of the break is that we don't retry, i.e. we abort the transaction. If you prefer you can throw an isolation_exception when the user uses break or continue instead of asserting it don't use it.
I'm pretty sure you're trying to stop the user from shooting himself in the foot here. instead of "break", the user could also "return" from a retry clause. or throw an exception and absorbing it outside of the transactino scope, with similar results. I think "break" should break, not assert and not throw, just because we think this is what the user should have done.
try{ //... }catch(my_exc &){ transaction{ //... }retry{ throw; } }
is it expected behaviour that this rethrows isolation_exception, not my_exc?
yes, IMO the exception must be isolation_exception, or a specific exception the library documents and make the outer transaction to abort.
OK. moved the user's retry code back into the catch-clause. best, Stefan

Hi, ----- Original Message ----- From: <strasser@uni-bremen.de> To: <boost@lists.boost.org> Sent: Sunday, February 21, 2010 11:21 PM Subject: Re: [boost] [transact] transaction language macros (was: Re: [transact] code in sandbox)
the changes we've discussed:
I have used this enumeration enum control_flow { none, break_, continue_ }; I suggest you use the same to avoid code using constants as 0, 1,2.
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
Yes, we can. But the result of the break is that we don't retry, i.e. we abort the transaction. If you prefer you can throw an isolation_exception when the user uses break or continue instead of asserting it don't use it.
I'm pretty sure you're trying to stop the user from shooting himself in the foot here. instead of "break", the user could also "return" from a retry clause. or throw an exception and absorbing it outside of the transactino scope, with similar results. I think "break" should break, not assert and not throw, just because we think this is what the user should have done.
The difference is that the user would not write a retry block on a sequential program, retry is specific of transactions. As we are not used to work with retry, maybe the best is to allow it to choose if s/he prefer to let break/continue to be used, asserted or throw an isolation_exception. The default could be to exit the user loop. What do you think?
try{ //... }catch(my_exc &){ transaction{ //... }retry{ throw; } }
is it expected behaviour that this rethrows isolation_exception, not my_exc?
yes, IMO the exception must be isolation_exception, or a specific exception the library documents and make the outer transaction to abort.
OK. moved the user's retry code back into the catch-clause.
A minor detail to the new implementation. The body of a retry statement should IMO be a compound statement if we want follow the C++ catch grammar. }catch(boost::transact::isolation_exception &___i){ \ ___i.unwind<TXMGR>(); \ do{ \ ___control=1; \ if(false);else Instead of if(false);else you can do try {throw;}catch (boost::transact::isolation_exception &) I have a question related to unwind. Instead of }catch(boost::transact::isolation_exception &___i){ \ ___i.unwind<TXMGR>(); \ I have the equivalent of } catch (boost::transact::isolation_exception &) { \ if (__tx.is_nested()) throw; \ is_nested returns true if the transaction is not a root tra,nsaction. Does unwind do exactly the same? If this is not the case could you explain what unwind does? Best, Vicente

Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
I'm pretty sure you're trying to stop the user from shooting himself in the foot here. instead of "break", the user could also "return" from a retry clause. or throw an exception and absorbing it outside of the transactino scope, with similar results. I think "break" should break, not assert and not throw, just because we think this is what the user should have done.
The difference is that the user would not write a retry block on a sequential program, retry is specific of transactions. As we are not used to work with retry, maybe the best is to allow it to choose if s/he prefer to let break/continue to be used, asserted or throw an isolation_exception. The default could be to exit the user loop. What do you think?
that's even worse ;) my point is that rearely will anyone ever intentionally use this, so it should behave as you would expect without learning anything, or be able to be documented easily. 1. "loop control statements can be used in transaction/retry blocks. they affect loops around it." 2. "when loop control statements are used in transaction blocks, they affect... Loop control statements in retry blocks result in undefined behaviour." (assertion in debug mode) 3. "when loop...in transaction blocks... the behaviour of control statements in retry blocks can be configured. here is how you configure it: ......."
try {throw;}catch (boost::transact::isolation_exception &)
this has runtime overhead. throwing is an external function call on the compilers I use, even in this trivial case. I don't know if there is another way to enforce compound statements (and I don't know why "catch" requires one).
I have a question related to unwind. Instead of
}catch(boost::transact::isolation_exception &___i){ \ ___i.unwind<TXMGR>(); \
I have the equivalent of } catch (boost::transact::isolation_exception &) { \ if (__tx.is_nested()) throw; \
not equivalent. "is_nested() throw;" would always unwind the traqnsaction stack completely, up to the root transaction. but sometimes a conflict is detected that is e.g. caused by 2 contending nested transactions, so only one nested transactions needs to be repeated. how unwind() works, pseudocode: transaction *txonstack= <parent transaction of active_transaction(), or 0>; throw isolation_exception(txonstack); ... ... catch(isolation_exception &i){ i.unwind(); assert(&active_transaction() == txonstack); } so the code throwing the isolation_exception(typically the RM) can decide which transaction will be active when unwind() returns.

Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
I agree this doesn't means that the transaction can not be removed shortly thereafter, but neither the oposite, that is that the transaction will be destructed shortly thereafter. The transaction could be reused if the implementation is improved. I have already, as I showed in a previous mail, moved the transaction declaration out of the try, so the transaction out leaves the retry, and the internal loop.
I think there is another problem with the restart-approach, unless the following use case should be invalid: transaction{ ... }retry{ transaction{ ... } throw 1; } with the current approach, inside the retry-clause there is a current transaction which is not active. the second transaction-scope tries to create a nested transaction of the inactive transaction - and throws no_active_transaction. is it a valid use case to run a transaction inside a retry-clause? if not, why not? (btw, I'm going to change TM::active_transaction() to TM::current_transaction() and add a bool basic_transaction::active(), since the current transaction is not necessarily active at all times, like in a retry clause)

Hi, ----- Original Message ----- From: <strasser@uni-bremen.de> To: "vicente.botet" <vicente.botet@wanadoo.fr> Sent: Saturday, March 06, 2010 3:09 PM Subject: Re: [boost] [transact] transaction language macros (was: Re: [transact] code in sandbox)
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
Zitat von "vicente.botet" <vicente.botet@wanadoo.fr>:
I agree this doesn't means that the transaction can not be removed shortly thereafter, but neither the oposite, that is that the transaction will be destructed shortly thereafter. The transaction could be reused if the implementation is improved. I have already, as I showed in a previous mail, moved the transaction declaration out of the try, so the transaction out leaves the retry, and the internal loop.
I think there is another problem with the restart-approach, unless the following use case should be invalid:
transaction{ ... }retry{ transaction{ ... } throw 1; }
with the current approach, inside the retry-clause there is a current transaction which is not active. the second transaction-scope tries to create a nested transaction of the inactive transaction - and throws no_active_transaction.
is it a valid use case to run a transaction inside a retry-clause?
retry blocks should be quite simple, modify some local variables, update some policies on the current transactions and so on. In principle this should not need a transaction to achieve its purpose. Have you find a use case that needs a transaction?
I think so:
tx::numeric<int> nr_of_retries=0;
void thread_start(){ //start of multiple threadsx
transaction{ ... }retry{
transaction{ ++nr_of_retries; }
}
}
What is the goal of increassing a shared variable nr_of_retries on the retry block, statistics? I thought you were looking for a real use case, for which we need a transaction on the retry block to avoid starvation. I will add that the schema is recursive without an end transaction{ ... } retry { transaction{++nr_of_retries;} retry { transaction{++nr_of_retries;} retry { transaction{++nr_of_retries;} retry { ... } } } } This why I said that it is not a good principle to repare something that fails using the same failing technique.
yes, you could achieve the same thing by counting nr_of_retries locally and adding it once the transaction has succeeded, but my point is not that this use case is absolutely necessary, but that I can't find a good reason to prohibit it.
I don't dsee any propblem to prohibit a construction that does not have real use cases.
if I'd see see the above code without reading any documentation, I'd expect that the transaction inside the retry block starts another root transaction. and in fact, that was the behaviour until we decided that the transaction should be accessible in the retry-clause and be restarted after it.
The fact the preceding approach allows this construction is not an hint it was supperior approach, as I don't see yet the need to allow the construction. I agree however that if we need to allow this, the transaction should be a root transaction otherwise the transaction effect cound not influence the behaviour of the other transactions. IMO the retry block should access local variables, thread specific variables or transaction specific functions.
if not, why not?
Well, I would say that it is not a good principle to repare something that fails using the same failing technique. The problem a 'nested' transaction on a non active transaction introduce is the visibility of the 'nested' transaction.
I'd expect it to be a root transaction, similar to:
try{ transaction tx;
tx.commit(); }catch(isolation_exception &){ transaction tx; ... tx.commit(); }
while the current behvaiour is:
transaction tx; try{ ... tx.commit(); }catch(isolation_exception &){ transaction tx; ... tx.commit(); }
when you see the macro-code above, would you expect it to behave like the second version?
For the moment I expect it will fail, and this can only be provided in the second case.
on a seperate note, I noticed that .NET has a namespace System.Transactions which seems to be quite similar to what we're trying to achieve with Boost.Transact. on first sight there isn't much to learn from, but maybe one of us should have a closer look.
Could you give some pointers? Thanks, Vicente
participants (7)
-
Bob Walters
-
Paul A. Bristow
-
Seth Bunce
-
Stewart, Robert
-
strasser@uni-bremen.de
-
Vicente Botet Escriba
-
vicente.botet