www.digitalmars.com         C & C++   DMDScript  

D - Transactions

reply nicO <nicolas.boulay ifrance.com> writes:
Few times ago, i will see how to use C long jump to restore a crashing
program. So i rediscover the concept of transaction.

I think it could be usefull to be implementent in the language built-in.
You need at least 3 new keys words.

BEGIN_TRANSACTION, ABORT_TRANSACTION, END_TRANSACTION

The goal is to create a protected environnement, if the transaction
failed for what ever raison the initial state (just before the
begin_transaction) is restore. We could imagine in a program with GUI,
each fonction under each menu are a transaction so instead of a core
dump you just have an error message, if the procedure failed. It could
be interresting for Deamon, too, to avoid DoS. 

I think i could be easly done as for fork() system call are made under
Linux. Each data section became read-only, if a write occur it's trap.
Then the system use an other page and copy the initial value and then
make the write. If the transaction failed the page is "forget". If it
reach the end_transaction, the memory page are swaped and the old one is
given back to the free memory tank.

I think it's an interresting way to write more secure program.

nicO
Aug 19 2001
parent reply "Walter" <walter digitalmars.com> writes:
How does this differ from try-catch-finally?

"nicO" <nicolas.boulay ifrance.com> wrote in message
news:3B8016F2.E5DDCD04 ifrance.com...
 Few times ago, i will see how to use C long jump to restore a crashing
 program. So i rediscover the concept of transaction.

 I think it could be usefull to be implementent in the language built-in.
 You need at least 3 new keys words.

 BEGIN_TRANSACTION, ABORT_TRANSACTION, END_TRANSACTION

 The goal is to create a protected environnement, if the transaction
 failed for what ever raison the initial state (just before the
 begin_transaction) is restore. We could imagine in a program with GUI,
 each fonction under each menu are a transaction so instead of a core
 dump you just have an error message, if the procedure failed. It could
 be interresting for Deamon, too, to avoid DoS.

 I think i could be easly done as for fork() system call are made under
 Linux. Each data section became read-only, if a write occur it's trap.
 Then the system use an other page and copy the initial value and then
 make the write. If the transaction failed the page is "forget". If it
 reach the end_transaction, the memory page are swaped and the old one is
 given back to the free memory tank.

 I think it's an interresting way to write more secure program.

 nicO

Aug 21 2001
parent reply Russell Bornschlegel <kaleja estarcion.com> writes:
Walter wrote:
 
 How does this differ from try-catch-finally?
 
 "nicO" <nicolas.boulay ifrance.com> wrote in message
 news:3B8016F2.E5DDCD04 ifrance.com...
 Few times ago, i will see how to use C long jump to restore a crashing
 program. So i rediscover the concept of transaction.

 I think it could be usefull to be implementent in the language built-in.
 You need at least 3 new keys words.

 BEGIN_TRANSACTION, ABORT_TRANSACTION, END_TRANSACTION


Walter-- Things within the try block that executed before the exception fired aren't "undone" with try-catch-finally; the usual database sense of "transaction" connotes atomicity: it requires that either everything within the transaction succeeds, or that nothing happens: transaction { if (take_money_out_of_walters_account() == FAILURE) { abort_transaction; } if (put_money_into_russells_account() == FAILURE) { abort_transaction; } } Here, if the money comes out of your account successfully, then for whatever reason, it can't be put into my account, we "roll back" the whole transaction and the money goes back into your account. I don't think that this can be done in a C-like language, not in the general case of undoing any conceivable action.
 I think i could be easly done as for fork() system call are made under
 Linux. Each data section became read-only, if a write occur it's trap.
 Then the system use an other page and copy the initial value and then
 make the write. If the transaction failed the page is "forget". If it
 reach the end_transaction, the memory page are swaped and the old one is
 given back to the free memory tank.


Any function called during such a transaction that had side effects would be problematic with a scheme like this. This is only useful for rolling back changes to process local memory, and that's doable with a class object that's "double-buffered" -- i.e. it keeps "current state" and "state-in-progress," and once "state-in-progress" is updated safely, the buffers are swapped atomically. -R
Aug 21 2001
parent reply nicO <nicolas.boulay ifrance.com> writes:
Russell Bornschlegel a écrit :
 
 Walter wrote:
 How does this differ from try-catch-finally?

 "nicO" <nicolas.boulay ifrance.com> wrote in message
 news:3B8016F2.E5DDCD04 ifrance.com...
 Few times ago, i will see how to use C long jump to restore a crashing
 program. So i rediscover the concept of transaction.

 I think it could be usefull to be implementent in the language built-in.
 You need at least 3 new keys words.

 BEGIN_TRANSACTION, ABORT_TRANSACTION, END_TRANSACTION


Walter-- Things within the try block that executed before the exception fired aren't "undone" with try-catch-finally; the usual database sense of "transaction" connotes atomicity: it requires that either everything within the transaction succeeds, or that nothing happens: transaction { if (take_money_out_of_walters_account() == FAILURE) { abort_transaction; } if (put_money_into_russells_account() == FAILURE) { abort_transaction; } } Here, if the money comes out of your account successfully, then for whatever reason, it can't be put into my account, we "roll back" the whole transaction and the money goes back into your account. I don't think that this can be done in a C-like language, not in the general case of undoing any conceivable action.
 I think i could be easly done as for fork() system call are made under
 Linux. Each data section became read-only, if a write occur it's trap.
 Then the system use an other page and copy the initial value and then
 make the write. If the transaction failed the page is "forget". If it
 reach the end_transaction, the memory page are swaped and the old one is
 given back to the free memory tank.


Any function called during such a transaction that had side effects would be problematic with a scheme like this. This is only useful for rolling back changes to process local memory, and that's doable with a class object that's "double-buffered" -- i.e. it keeps "current state" and "state-in-progress," and once "state-in-progress" is updated safely, the buffers are swapped atomically.

In fact, it's hard to do it with IO function. That sure. But maybe such function couldn't be allowed inside transaction. Or compiler could detect that and double buffer each write. The problem are for read. I think it could be done for file (like for data base at the beginning of the transaction). Maybe IO function should be rewrite. Concerning all access to the network, i understand that couldn't be handel. But that's a big beginning. nicO
 -R

Aug 22 2001
parent reply Eric Gerlach <egerlach canada.com> writes:
 Russell Bornschlegel a écrit :

 <snip>
 
 In fact, it's hard to do it with IO function. That sure. But maybe such
 function couldn't be allowed inside transaction. Or compiler could
 detect that and double buffer each write. The problem are for read.
 
 I think it could be done for file (like for data base at the beginning
 of the transaction). Maybe IO function should be rewrite. Concerning all
 access to the network, i understand that couldn't be handel. But that's
 a big beginning.
 
 nicO

I'd never thought of it before, even though I do lots of work with databases, but this could be an interesting idea. IMHO, it would be very, very difficult to implement with the syntax you suggest, however. I think if a more complex syntax were used it would be easier to implement it. Consider a syntax similar to Java's 'synchronised': int foo(int a) transaction { return perform_global_operation(a); } rollback { undo_global_operation(a); } void main() { transaction { if (3 == foo(2)) { rollback; } /* do more stuff, it's okay */ } } You could only have functions which support transactions within a transaction block. The 'rollback' statement calls the rollback portions of the function calls (in reverse call-order?), and they are responsible for undoing what they did. Now, I don't think that this fits into the scheme of D, but it's a really neat idea. I wouldn't mind seeing it if it's not too difficult to implement though. It could be really useful. Eric
Aug 25 2001
parent reply Dan Hursh <hursh infonet.isl.net> writes:
Eric Gerlach wrote:
 
 Russell Bornschlegel a écrit :

 <snip>

 In fact, it's hard to do it with IO function. That sure. But maybe such
 function couldn't be allowed inside transaction. Or compiler could
 detect that and double buffer each write. The problem are for read.

 I think it could be done for file (like for data base at the beginning
 of the transaction). Maybe IO function should be rewrite. Concerning all
 access to the network, i understand that couldn't be handel. But that's
 a big beginning.

 nicO

I'd never thought of it before, even though I do lots of work with databases, but this could be an interesting idea. IMHO, it would be very, very difficult to implement with the syntax you suggest, however. I think if a more complex syntax were used it would be easier to implement it. Consider a syntax similar to Java's 'synchronised': int foo(int a) transaction { return perform_global_operation(a); } rollback { undo_global_operation(a); } void main() { transaction { if (3 == foo(2)) { rollback; } /* do more stuff, it's okay */ } } You could only have functions which support transactions within a transaction block. The 'rollback' statement calls the rollback portions of the function calls (in reverse call-order?), and they are responsible for undoing what they did. Now, I don't think that this fits into the scheme of D, but it's a really neat idea. I wouldn't mind seeing it if it's not too difficult to implement though. It could be really useful. Eric

Well this topic doesn't belong here, but I like it. How about a way of mark data to be used in the transaction and let the compiler deal with make copies and restoring values. int x, y, z; string m, n, o; // do cool things transaction{ // too long of a key word mark x; // call it mark because preserve is also too long mark y; // We want to be able to restore these values mark m, n; // we might as well allow marking multiples z = mess_with(m, x); // we won't preserve z if(z != GOOD_THINGS){ o = "first operation failed"; // o isn't preserved rollback(o); // restore uncommitted values and throw // RollBack exception with o as message } commit(m); // commit only the change to m so far (not x) z = mess_with(n, y); if(z != GOOD_THING){ rollback(o="second operation bombed"); } commit(); // commit all marked vars in this transaction block. // I don't know how to do "all mark vars in this // transaction and enclosing transactions. Maybe // named transaction blocks? }catch(RollBack e){ log("Bad things man [" + string(z) + "]:" + e.msg()); } Uncommitted marked variables would automatically roll back when leaving the transaction block without a call to rollback, but I can decide if an exception should be throw then. Mark would probably require types to have some form of duplication method so it can copy more than just a reference. The transaction block is really the only thing special here. Rollback() could be a function. Mark would probably be special unless generic programming is supported well enough, but even then I guess rollback and mark would need access to some special context info introduced by any enclosing transaction blocks. In any case, this is all academic. Dan
Aug 25 2001
parent nicO <nicolas.boulay ifrance.com> writes:
Dan Hursh a écrit :
 
 Eric Gerlach wrote:
 Russell Bornschlegel a écrit :

 <snip>

 In fact, it's hard to do it with IO function. That sure. But maybe such
 function couldn't be allowed inside transaction. Or compiler could
 detect that and double buffer each write. The problem are for read.

 I think it could be done for file (like for data base at the beginning
 of the transaction). Maybe IO function should be rewrite. Concerning all
 access to the network, i understand that couldn't be handel. But that's
 a big beginning.

 nicO

I'd never thought of it before, even though I do lots of work with databases, but this could be an interesting idea. IMHO, it would be very, very difficult to implement with the syntax you suggest, however. I think if a more complex syntax were used it would be easier to implement it. Consider a syntax similar to Java's 'synchronised': int foo(int a) transaction { return perform_global_operation(a); } rollback { undo_global_operation(a); } void main() { transaction { if (3 == foo(2)) { rollback; } /* do more stuff, it's okay */ } } You could only have functions which support transactions within a transaction block. The 'rollback' statement calls the rollback portions of the function calls (in reverse call-order?), and they are responsible for undoing what they did. Now, I don't think that this fits into the scheme of D, but it's a really neat idea. I wouldn't mind seeing it if it's not too difficult to implement though. It could be really useful. Eric

Well this topic doesn't belong here, but I like it. How about a way of mark data to be used in the transaction and let the compiler deal with make copies and restoring values. int x, y, z; string m, n, o; // do cool things transaction{ // too long of a key word mark x; // call it mark because preserve is also too long mark y; // We want to be able to restore these values mark m, n; // we might as well allow marking multiples z = mess_with(m, x); // we won't preserve z if(z != GOOD_THINGS){ o = "first operation failed"; // o isn't preserved rollback(o); // restore uncommitted values and throw // RollBack exception with o as message } commit(m); // commit only the change to m so far (not x) z = mess_with(n, y); if(z != GOOD_THING){ rollback(o="second operation bombed"); } commit(); // commit all marked vars in this transaction block. // I don't know how to do "all mark vars in this // transaction and enclosing transactions. Maybe // named transaction blocks? }catch(RollBack e){ log("Bad things man [" + string(z) + "]:" + e.msg()); } Uncommitted marked variables would automatically roll back when leaving the transaction block without a call to rollback, but I can decide if an exception should be throw then. Mark would probably require types to have some form of duplication method so it can copy more than just a reference. The transaction block is really the only thing special here. Rollback() could be a function. Mark would probably be special unless generic programming is supported well enough, but even then I guess rollback and mark would need access to some special context info introduced by any enclosing transaction blocks. In any case, this is all academic.

If you put many commit, in fact you do 2 separate transaction. Transaction look like synchronise because it should do the same things, too (atomic behavior). So commit could be down at the end of the transaction. As said before, rollback memory (variable,...) is in fact very easy. As maid for fork() under linux, all memory page are marked read-only, if a write occure, it is done in an other memory page. Then, if the transaction is abort, the new pages are destroy. If it commit, the old page is destroy and the new pages are used. No need of rollback function, it save a lot of code to write. Rollback function used as this look like exception handler. nicO
 Dan

Aug 26 2001