<div dir="ltr">Hi everyone,<br><br>I want to finally announce some work I did as part of my master's thesis [1]. It's an extension to GHC's STM implementation. The main idea is to introduce a new primitive function<br><br><font face="monospace, monospace">    atomicallyWithIO :: STM a -> (a -> IO b) -> IO b</font><br><br>Like the existing <font face="monospace, monospace">atomically</font> operation, <font face="monospace, monospace">atomicallyWithIO</font> performs an STM computation. Additionally, it takes a <b>finalizer</b>, which is an arbitrary I/O action that can depend on the result of the STM computation, and combines it with the transaction in such a way that:<br><ol><li>The finalizer is only performed if the STM transaction is guaranteed to commit.<br></li><li>The STM transaction only commits if the finalizer finishes without raising an exception.</li></ol><div><br></div><div><br></div>A motivation of why this is useful: <br><div><div><br></div><div>Say we want to save the results of some STM transaction, i.e. persist them to disk. Currently, this is not possible in a transactionally safe way. The naive approach would be to first atomically perform some STM computation and then independently serialise its result:<br></div><div><div><br></div><div><font face="monospace, monospace">    do </font></div><div><font face="monospace, monospace">        x <- atomically m</font></div><div><font face="monospace, monospace">        serialize x</font></div><div><br></div><div>There are two issues with this. First, another thread could perform a second transaction and serialise it before we have finished serialising ours, which could result in an inconsistent state between the memory of our program and what is stored on disk. Secondly, the function <font face="monospace, monospace">serialize</font> might not terminate at all; it could throw an exception or its thread could die. Again we would end up with an inconsistent state and possibly data loss.</div><div><br></div><div>We might try to simply move serialisation into the atomic block, but to do this we have to circumvent the types, which should already be a huge red flag:</div><div><br></div><div><font face="monospace, monospace">    atomically $ do</font></div><div><font face="monospace, monospace">        x <- m</font></div><div><font face="monospace, monospace">        unsafeIOToSTM (serialize x)</font></div><div><br></div><div>The problem here is of course that an STM transaction can retry many times, which will also result in <font face="monospace, monospace">serialize</font> being executed many times, which is probably not something we want. Furthermore, if the thread receives an asynchronous exception, the transaction will abort in an orderly fashion, while <font face="monospace, monospace">serialize</font>, with its irrevocable side effects, cannot be undone.</div><div><div><br></div><div><div>But with finalizers, the solution is easy:</div><div><br></div><div><font face="monospace, monospace">    atomicallyWithIO m serialize</font><br><br></div><div><br></div><div>I've written a small example application that uses finalizers and other stuff from my thesis to build a lightweight STM database framework: [2]</div><div><br></div><div>There are more possible uses cases besides serialisation (such as interactive transactions) and some interesting issues surrounding finalizers (like nesting of transactions) which are discussed in greater detail in my thesis [1], which also includes a formal operational semantics of the whole thing.<br></div><div><br></div><div>I have implemented finalizers in my fork of GHC [3], if any of you want to play around with them yourself. The atomicallyWithIO function is exported from the GHC.Conc.Sync module.</div><div><br></div><div><br></div><div>Cheers,</div><div>Michael</div><div><br><br>[1] <a href="https://github.com/mcschroeder/thesis" target="_blank">https://github.com/mcschroeder/thesis</a></div><div>[2] <a href="https://github.com/mcschroeder/social-example" target="_blank">https://github.com/mcschroeder/social-example</a></div><div>[3] <a href="https://github.com/mcschroeder/ghc" target="_blank">https://github.com/mcschroeder/ghc</a><br><br></div></div></div></div></div></div>