Intimately, the post is actually the best on this laudable topic. I harmonize with your conclusions and will eagerly look forward to your future updates. Saying thanks will not just be adequate, for the fantastic lucidity in your writing.[link:www.monclerjacketsstyles.com]
I've updated the code for F# 2.0 [1].
Greg, I'd like to add this to FSharp.Monad [2]. Would you mind?
[1] [link:dl.dropbox.com]
[2] [link:github.com]
Greg, I'd like to add this to FSharp.Monad [2]. Would you mind?
[1] [link:dl.dropbox.com]
[2] [link:github.com]
Good article! Thank you so much for sharing this post.Your views truly open my mind.
Thanks, I completely agree with your point of view.
gneverov said:
Yes, that's right. Haskell is able to do this because it is a pure functional language and all side effects are encapsulated by the IO monad which gives side effecting computations a different static type.
welcome to china wholesale direct from China!
china wholesale shop supply any kinds goods!
china wholesale shop supply any kinds goods!
You've been kicked (a good thing) - Trackback from DotNetKicks.com
Introduction
F# has the async computation expression for writing parallel programs. Async achieves concurrency...
F# has the async computation expression for writing parallel programs. Async achieves concurrency...
Synopsis I gave an hour long talk today, here at Atalasoft, on Concurrency in F# . It featured some slides
PingBack from [link:frankthefrank.info]
Last night the wmassdevs Group hosted a Clojure presentation by Rich Hickey . Clojure was born of Rich's
Since I have a rather long commute to and from work, I have the opportunity to get caught up on all sorts
PingBack from [link:dougfinke.com]
PingBack from [link:msdnrss.thecoderblogs.com]
Greg Neverov (of active patterns fame) has placed an implementation of Software Transactional Memory
Greg Neverov (of active patterns fame) has placed an implementation of Software Transactional Memory
I wonder if F# could be enhanced to have an unsafe mode, a bit like C#, with annotations for "unsafe" side-effect-allowing code. Code not thus annotated would be statically checked just as in Haskell.
Yes, that's right. Haskell is able to do this because it is a pure functional language and all side effects are encapsulated by the IO monad which gives side effecting computations a different static type. Since F# is an impure language and by nature allows side effects anywhere, there is no easy way for the type system to prevent side effects in a transaction statically.
Great post! Does your prior comment mean that unlike the Haskell STM, this F# one does not statically prevent a programmer from including IO or a side effect inside a transaction?
The current implementation does not rollback side effects (I/O and non-transacted memory) when a transaction re-executes.
Well, my point is: with the optimistic strategy used by STM, if a value has mutable state _inside_ (like the closure here: let x = ref 0 in fun () -> incr x; !x), even those failed commits (or even purly access) can still change its content.
No, because you are using a Ref to store the counter not a TVar -- only TVars are synchronized under concurrent access. The TVar, f, that you use does not in fact do anything because although you write to f the value of f never changes: it is always a closure that increments x.
What happens if you define a new tvar as
will they return continuous int sequence?
let f = newRef (let x = ref 0 in fun () -> incr x; !x)
and run several copy of
stm { let! ff = readRef f
let v = ff ()
do! writeRef f ff
return v }
will they return continuous int sequence?
Topic tags
- f# × 3660
- compiler × 263
- functional × 199
- c# × 119
- websharper × 114
- classes × 96
- web × 94
- book × 84
- .net × 82
- async × 72
- parallel × 43
- server × 43
- parsing × 41
- testing × 41
- asynchronous × 30
- monad × 28
- ocaml × 26
- tutorial × 26
- haskell × 25
- workflows × 22
- html × 21
- linq × 21
- introduction × 19
- silverlight × 19
- wpf × 19
- fpish × 18
- collections × 14
- pipeline × 14
- templates × 12
- monads × 11
- opinion × 10
- reactive × 10
- plugin × 9
- scheme × 9
- sitelets × 9
- solid × 9
- basics × 8
- concurrent × 8
- deployment × 8
- how-to × 8
- python × 8
- complexity × 7
- javascript × 6
- jquery × 6
- lisp × 6
- real-world × 6
- workshop × 6
- xaml × 6
- conference × 5
- dsl × 5
- java × 5
- metaprogramming × 5
- ml × 5
- scala × 5
- visual studio × 5
- formlets × 4
- fsi × 4
- lift × 4
- sql × 4
- teaching × 4
- alt.net × 3
- aml × 3
- enhancement × 3
- list × 3
- reflection × 3
- blog × 2
- compilation × 2
- computation expressions × 2
- corporate × 2
- courses × 2
- cufp × 2
- enterprise × 2
- entity framework × 2
- erlang × 2
- events × 2
- f# interactive × 2
- fsc × 2
- google maps × 2
- html5 × 2
- http × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- keynote × 2
- mvc × 2
- numeric × 2
- obfuscation × 2
- oop × 2
- packaging × 2
- pattern matching × 2
- pipelines × 2
- rx × 2
- script × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- type × 2
- type provider × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- addin × 1
- agents × 1
- agile × 1
- android × 1
- anonymous object × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net mvc × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- b-tree × 1
- bistro × 1
- bug × 1
- camtasia studio × 1
- canvas × 1
- class × 1
- client × 1
- clojure × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- combinator × 1
- confirm × 1
- constructor × 1
- continuation-passing style × 1
- coords × 1
- coursera × 1
- csla × 1
- css × 1
- data × 1
- database × 1
- declarative × 1
- delete × 1
- dhtmlx × 1
- discriminated union × 1
- distance × 1
- docs × 1
- documentation × 1
- dol × 1
- domain × 1
- du × 1
- duf-101 × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- error × 1
- etw × 1
- euclidean × 1
- event × 1
- example × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- fantomas × 1
- fear × 1
- float × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- function × 1
- functional style × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- google × 1
- group × 1
- hash × 1
- history × 1
- hosting × 1
- httpcontext × 1
- https × 1
- hubfs × 1
- ie 8 × 1
- if-doc × 1
- inheritance × 1
- installer × 1
- interpreter × 1
- io × 1
- ios × 1
- ipad × 1
- kendo × 1
- learning × 1
- licensing × 1
- macro × 1
- macros × 1
- maps × 1
- markup × 1
- marshal × 1
- math × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- multidimensional × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- nested × 1
- nested loops × 1
- node × 1
- object relation mapper × 1
- object-oriented × 1
- offline × 1
- option × 1
- orm × 1
- osx × 1
- owin × 1
- paper × 1
- parameter × 1
- performance × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- programming × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- ptvs × 1
- quant × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- real-time × 1
- reference × 1
- restful × 1
- round table × 1
- runtime × 1
- scriptcs × 1
- scripting × 1
- service × 1
- session-state × 1
- sitelet × 1
- stickynotes × 1
- stress × 1
- strong name × 1
- structures × 1
- tdd × 1
- template × 1
- tracing × 1
- tsunamiide × 1
- type inference × 1
- type providers × 1
- upload × 1
- vb × 1
- vb.net × 1
- vector × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio shell × 1
- visualstudio × 1
- web api × 1
- webapi × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- xml × 1
|
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us |
Built with WebSharper |
I have written a library for using software transactional memory in F#. The library exposes memory transactions as a monad using F#’s new computation expression feature. It can be downloaded here.
Software transactional memory (STM) is an approach to concurrent programming that avoids the use of traditional, error-prone locks to control access to shared memory. Instead of using locks a programmer specifies sections of code that will make atomic changes to shared memory.
The design and implementation of this F# library is blatantly copied from the paper Composable Memory Transactions. I am enormously grateful to the authors of this paper whose clear and precise explanation made the implementation of this complicated piece of software almost trivial.
The low-level STM machinery is implemented in the file stm.cs. It is written in C# mainly because I was working off existing code I’d written some time ago, but there’s no reason why it could not be written in F#. It is all managed code and internally uses monitors for synchronization. It currently uses a very coarse grained lock meaning that only one transaction can commit at once and every waiting thread is resumed when a transaction completes, but this can be improved with more work. The implementation follows the description in the paper.
The F# monadic interface is in the file stm.fs. It imports definitions from the C# assembly and defines the monad type
Stm<'a>with corresponding builder class to enable computation expression syntax. An issue with the implementation of STM systems is how to access the transaction log to make reads and writes during a transaction. Two common approachs are to use thread-local storage which is slow, or implicitly pass the transaction log as an extra parameter to every function, which requires code generation. The advantage of using computation expressions (or monadic do notation) is that the transaction log is explicitly passed between functions by the programmer’s code, thereby avoiding compiler modification, but also avoiding the need for the programmer to manually manage the transaction log parameter passing scheme as well.With these two layers in place one can write STM code in F#. Transacted memory locations have the type
TVar<'a>. This is a reference cell likeRef<'a>in F# but its value can only be read and written from inside a transaction. A TVar is created using thenewTVarfunction.This code is similar to , but being a TVar instead of a Ref, the location
xcan be safely shared between multiple threads.A transaction is defined using a computation expression. A transaction is a value of type
Stm<'a>which represents a computation that when executed will make an atomic change to the values of zero or more TVars and then return a value of type'a. The type system ensures at compile-time that all reads and writes to a TVar only take place inside a transaction. The functionsreadTVarandwriteTVarread and write a TVar respectively. They are analogous tolet v = !xandx := vfor regular reference cells.Let’s define a function that increments the value stored in a TVar.
let incr x = stm { let! v = readTVar x do! writeTVar x (v+1) return v }The syntax
stm { … }is an F# computation expression using the 'stm' monad, which means that this is a memory transaction (as opposed to an async work unit or something). The value of the location is read and stored inv. The incremented value is then written back to the location andvis returned as the result of the computation. When executed the read and write happen as an atomic block which means logically that an interleaving thread won’t access the location between the read and write.A transaction is executed by the
atomicallyfunction.The composability of STM can be seen by the ability to compose
incrinto a larger transaction that increments a location twice.let incr2 x = stm { let! _ = incr x let! v = incr x return v }Imagine that
incrwas part of the public interface of a module. Ifincrused a lock to perform the read and write atomically then it would be impossible for users of the library to writeincr2unless the module also exposed its locks through a public interface, which would make programming intricate for the user.If you see any Haskell STM code, it is straightforward to translate this into F#. For example the paper gives the implementation of an MVar as a code example. The paper describes an MVar as
An MVar is a mutable location like a TVar, except that it may be either empty, or full with a value. The takeMVar function leaves a full MVar empty, and blocks on an empty MVar. A putMVar on an empty MVar leaves it full, and blocks on a full MVar. So MVars are, in effect, a one-place channel.
MVars can be implemented in F# STM like so.
type MVar<'a> = TVar<option<'a> > let newEmptyMVar () = newTVar None let newFullMVar v = newTVar (Some v) let takeMVar mv = stm { let! v = readTVar mv return! match v with | Some a -> stm { do! writeTVar mv None return a } | None -> retry () } let putMVar mv a = stm { let! v = readTVar mv return! match v with | None -> writeTVar mv (Some a) | Some _ -> retry () }The
retryoperation is used to re-execute a transaction from the beginning. There is no point re-executing the transaction immediately because no memory locations will have changed. So the retry operation blocks until some memory changes that might allow the transaction to make further progress. This is how blocking is implemented with STM.Another feature of STM that makes it more composable than locks is how it handles blocking. The get/put MVar operations above are blocking operations. If an MVar library was written using locks and non-blocking versions of these operations were required then they would have to be implemented by the library developer and exposed as part of the library’s public interface. Not so with STM: if the MVar interface only exposes blocking get/put operations then it is possible for the user of the library to write the non-blocking versions, and vice-versa.
let tryTakeMVar mv = orElse (stm { let! v = takeMVar mv return Some v }) (stm { return None })The function is the non-blocking version of
takeMVar; it returns an option value depending on whether a value was available. The key here is theorElseoperation. This function takes two transactions and tries to execute the first. If the first fails with a retry/block then it executes the second. So in this example iftakeMVarretries/blocks then it will returnNone.Also included in the code download is the Santa program, a singly-linked list queue, and a fixed-length array queue.
Of course all these great benefits of STM come at a price, and that price is performance. An STM program is probably always going to be slower than a lock-based program. This STM implementation was engineered for correctness and design of the monadic interface and so probably has really bad performance.