Niners, please play with this (the C#/VB.net library may be easier to grasp given that Haskell is purely functional...). Tim and Simon need real world feedback. How would you use atomic blocks in your applications? What scenarios make use of atomic hard for you? See the links in the video post description. Also, please read the Composability paper (you don't have to be a scientist to understand it...)
Play with STM!
C
staceyw wrote:1) I missed a fundamental idea I think. It sound as if the last writer wins. So if I have an atomic block, I can make changes to state. But another thread can write to the state and commit, but the first atomic block will then rollback. What did I miss?
staceyw wrote:3) How does long running I/O and DB transactions play with this. So in a normal app, I would need both to be atomic. I need the in-memory objects to be atomic and the DB to be atomic in a single transaction. How does this work?
It seems to me that STM creates new problems with composability. You create two classes of code: atomic methods and non atomic methods.
Nonatomic methods can easily call atomic ones – the compiler could even automatically inject the atomic block if the programmer forgot.
Atomic methods and blocks cannot be allowed to call nonatomic code. The nonatomic code could do I/O or other irrevocable things that would be duplicated when the block had to retry.
This creates the exact problem that the “const” modifier has in C++. Your library writer has to anticipate everything you may want to do inside a transaction and provide you with an atomic version. By liberally making methods atomic, the author seriously limits future versions of the library because making a previously atomic method non-atomic is a breaking change.
In C++ they cop out and let you cast away the “constness” of a variable because there were too many places where the composition, exactly the problem you are trying to fix, didn’t work when you had one class of code that couldn’t call another class of code.
Its worse for STM because you can’t even cop out and say “use this sparingly.” Letting a transaction call nonatomic code creates the very race condition that you are trying to avoid, and makes them worse by running code many times that you have told the programmer to think about as only running once.
I am certain this will not be the first time this problem has come up, especially since you mention statically enforcing it in the compiler. I am just curious to hear what people think about what it will do to composability.
staceyw wrote:2) Charles, you had a good question that don't think was fully answered in the vid. Doing async in the block.atomic(){ // Do some stuff BeginXXX( delegate(int i) { // Do some stuff in callback. }}We do some stuff and then kick off an async operation. But then we exit the atomic block right away because the async operation does not block. So the callback is not executed in atomic anymore. And worse yet, the code appears as if it is still executed in the block. What I am missing? Or was the answer just to never do this?
Excellent video!I downloaded the C# code and took a quick look.Good work with the runtime proxy generation. Geez, it must have been painful writing all that emit IL code!I'll take a look at writing some Nemerle macros to make the syntax nice. I'm thinking macros along the lines of:foo = atomic(Foo(arg1, arg2))<==>foo = XAction.MakeFactory(typeof(Foo)).Create(arg1, arg2) : Foo;-------------------------atomic(foo.Bar(x,y,z))<==>XAction.Run(XStart(foo.Bar), x, y, z);--------------------------retry<==>XAction.Retry();--------------------------y = attempt { Get1, Get2 } (arg1, arg2)<==>y = XAction.Run(XAction.OrElse(new XStart(Get1), new XStart(Get2)), arg1, arg2) : int;
y = attempt {Get1, Get2, Get3} (arg1, arg2)<==>y = XAction.Run(XAction.OrElse(XAction.OrElse(new XStart(Get1), XAction.OrElse(new XStart(Get2), new XStart(Get3)))), arg1, arg2) : int;