| Safe Haskell | Safe-Inferred |
|---|
Control.Stasis
Contents
Description
Stasis is a modified implementation of Multi-version concurrency control MVCC. The original purpose of Stasis was to provide a mechanism for having safe, "mutable" variables WITHOUT any locking whatsoever.
How Stasis works
Given an object a.
When a is passed to a function it is wrapped in a Pod which creates a
stasis like environment for a.
At any point, any function that received the Pod can "update" it by
putting a new version of a.
i.e. a itself is not updated, it is replaced. So what the Pod does is
store the address of an a and an update changes the address the Pod points
to, to another a.
The address of a can only change to point to another value of a, i.e. it is type safe.
At any time, a function who's received a Pod can freeze it. In this case,
every time that function fetches a from the FrozenPod, the same version
that was frozen is always returned and at any time the function can also get
the latest version of a by getting the current non-frozen version.
Internally b maintains a map of addresses to frozen IDs, when a function freezes a version of a it stores the address of a with a unique freeze id that is returned to the function. The function must then pass the freeze id each time it wants to get the frozen version of a for that id. This also means that a function can lock multiple versions of a.
Originally the intention was to use pointers but IORefs works with atomic operations and achieves effectively the same thing. http://hackage.haskell.org/package/base-4.5.1.0/docs/Foreign-StablePtr.html http://hackage.haskell.org/package/base-4.6.0.1/docs/Data-IORef.html
- data Pod a = Stasis {}
- data FrozenPod a = FrozenPod {}
- stasis :: a -> IO (Pod a)
- version :: Pod a -> IO Int
- versionF :: FrozenPod a -> Int
- put :: a -> Pod a -> IO Bool
- merge :: a -> Pod a -> (a -> a -> a) -> IO Bool
- get :: Pod a -> IO a
- fetch :: FrozenPod a -> a
- freeze :: Ord a => Pod a -> IO (FrozenPod a)
- defrost :: Ord a => FrozenPod a -> IO (Pod a)
Primitives
Stasis has two primitives, a Pod and a FrozenPod.
A Pod is 'created from any value a, once created, only new versions of a can be placed in the same pod.
A Pod gets passed around in place of an ordinary value. Pods can be frozen
to create a FrozenPod, when frozen the value of a is stays the same
Operations
The type b supports 6 operations, stasis, put,get,fetch,freeze,defrost.
-
stasis- creates a newPodwhich can be passed around in place of a -
put- Put adds a new version of a to an existingPod -
get- Gets the current version of a from aPod -
freeze- Freezes a version of a in stasis, usedefroston the returnedFrozenPodto get the value back -
defrostReturns the version of a for the givenFrozenPod, i.e. it gets the value of a when it was frozen -
fetchthe same asdefrostbut leaves a in aFrozenPodwhich means it will not be garbage collected until it isdefrosted
Arguments
| :: a | A value that would otherwise be the new version |
| -> Pod a | The pod which will have its next version created from the function provided |
| -> (a -> a -> a) | A function which accepts the new value, current value and returns a new value made by merging the first two |
| -> IO Bool |
put works great when there is a single writer but if multiple threads are writing new versions it is sometimes
useful to be able to merge the current value with a new value to create a new version. This function allows for this
and enables the ability for multiple writers to create new versions while minimizing the probability of version loss