/
2013-02-28-resourcet-usage.html
140 lines (122 loc) · 8.69 KB
/
2013-02-28-resourcet-usage.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Qnikst blog - Resourcet usage</title>
<!-- Bootstrap -->
<link href="../css/bootstrap.min.css" rel="stylesheet" media="screen">
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="../js/bootstrap.min.js"></script>
</head>
<body>
<div class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<a class="brand" href="../">Qnikst blog</a>
<ul class="nav ">
<li class="active"><a href="../">Home</a></li>
<li><a href="../posts.html">Blog</a></li>
<li><a href="../projects.html">Projects</a></li>
<li><a href="../contact.html">Contacts</a></li>
</ul>
</div>
</div>
<div class="container">
<div class="page-header">
<h1>Resourcet usage <br /><small><strong>February 28, 2013</strong></small></h1>
</div>
<p>This post describes an interesting resourcet usecase that may be usefull due to the lack of liner-types support in haskell.</p>
<p>Lets look at the next problem. Assuming we have got a some sort of transaction mechanism that receives data and then perform transaction, but sometimes it may call callback to users code (originally it’s about <a href="http://github.com/qnikst/2pc">2pc</a> library).</p>
<p>So we have method:</p>
<pre><code>withWH0 :: (Binary b) -> ByteString -> (b -> IO Bool) -> IO ()
withWH0 = undefined</code></pre>
<p>Where Bool is result of transaction i.e. either we accept or decline transaction.</p>
<p>This variant is not good as callback is synchronous, we will not be able to run next transaction untill callback is finished. And it will be a bottleneck for the code.</p>
<p>There can be an interesting solution:</p>
<blockquote>
<p>one can provide an trasaction handler, i.e. a resource that for communication of the user code and library, this resource should be either accepted or declined. If that resource is ‘alive’ it means that user is working on transaction. If resource is not touched by user code a default action should be run, before resource will be cleared.</p>
</blockquote>
<p>The problem that we should somehow guarantee that user will free resource. One solution use linear types, but we don’t have them (or at least it’s too difficult for me and I don’t know how to do it). Another option is to use some kind of region library: one option is <a href="http://hackage.haskell.org/package/regions">regions</a> originaly proposed by <a href="http://okmij.org/ftp/Haskell/regions.html#light-weight">Oleg</a> but this package doesn’t work in recent ghc`s due some unsolved bugs, another option is <a href="http://hackage.haskell.org/package/resourcet">resourcet</a> package written by Michael Snoyman, this package doesn’t give so much guarantees as regions but at least it works. Resourcet package introduce ResourceT monad that forms a block where all resources that were registered in that blog will be closed as soon as compulation would left a block (there is an api for an early close but will not look at it now).</p>
<p>So we can guarantee that:</p>
<ol style="list-style-type: decimal">
<li>every resource registered in the block will be freed</li>
<li>resources freeing will be determinated in time/</li>
<li>resource will be freed only once (second call to release is noop)</li>
</ol>
<p>Here are minimal definition for what we need:</p>
<pre><code>data TH = TH -- ^ transaction handler
-- | accept transaction (send message via network)
accept = undefined
-- | decline transaction (send message via network)
decline = undefined
-- | real release (synonym for decline)
thRelease = undefined
withWH1 d cb = runResourceT $ do
let d' = decode d
th <- accure create thRelease
cb th d'</code></pre>
<p>Now we create a ResourceT region, register resource there and then call user code, our code is not as safe as resourcet release as user can call accept more that once, so we need to check if it was called.</p>
<p>The best thing that now client can use <code>resourceForkIO</code> to make withWH1 asynchronous.</p>
<p>But there is another issue: how user can queue some part of the messages?</p>
<p>TBD make nice picture</p>
<p>There is a solution: <code>unprotect</code> function. This function allowes you to degerister resource in the current resourcet block and then register it in another block, or register new release action.</p>
<p>Currently resourcet package lacks helpers that allowes to move resource to some other place, so one need to create that functions himself. Here are some advices how one can do it safely.</p>
<p>You need a structure that has the following interface:</p>
<ul>
<li>put :: s -> (a, IO ()) -> m () – put resource into storage</li>
<li>get :: s -> m (ReleaseKey a) – get resource and register it in current process</li>
</ul>
<p>Put and get should be atomic and mask exceptions, moreover if you can guarantee that message will be immidiatelly read by another process you should register datastructure in some higher level ResourceT block, this way you’ll guarantee that no resources will be left in store unfreed.</p>
<p>Here are some basic examples of such datastructures</p>
<p>At first let’s write an <code>InstantMVar</code>, this structure allow you to send message into other process, where it will be immideatelly read, so we don’t need to store that structure in ‘global’ resource block</p>
<pre><code>data InstanceMVar a = InstanceMVar (MVar (a,IO ())) (MVar ())
newMVar = InstanceMVar <$> newEmptyMVar <*> newEmptyMVar
put (InstanceMVar a b) (key, x) = mask_ $ unprotect key >>= putMVar a . ((,) x) >> takeMVar b
get (InstanceMVar a b) = mask_ $ takeMVar a >>= \(x, r) -> register r >> putMVar b () >> return x</code></pre>
<p>This code will guarantee that resource will be read, however it may lock.</p>
<p>Let’s write a <code>DelayedMVar</code> this structure will not block on write but have should be registered in global resourcet block</p>
<pre><code>newtype DelayedMVar a = DelayedMVar (MVar (a,IO ()))
newDelayedMVar = allocate (DelayedMVar <$> newEmptyMVar) (releaseDelayedMVar)
releaseDelayedMVar (DelayedMVar v) = maybe (return ()) (snd) =<< tryReadMVar v
put (DelayedMVar v) (key, x) = mask_ $ unprotect key >>= putMVar a . ((,) x)
get (DelayedMVar v) = mask_ $ takeMVar a >>= \(x, r) -> register r >> return x</code></pre>
<p>Same way one can write Channel or STM Channel, however channel is not fully safe unless chan is closable.</p>
<pre><code>newtype SChan a = SChan (SChan (a,IO ()))
newSChan = allocate (SChan <$> newChan) releaseChan
releaseChan (ChanMVar v) = go =<< tryReadChan v
where go Nothing = return ()
go (Just (x,a)) = a >> tryReadChan v >>= go</code></pre>
<p>There are many variants each with it’s own tradeoffs so one a free to build a way that matchs his task. But the next things should hold:</p>
<ol style="list-style-type: decimal">
<li>Datastructure should either guarantee that other side will read resource atomically or be registered in ResourceT monad</li>
<li>Read and writes should be exception safe</li>
</ol>
<p>Note. currently unprotect is not merged upstream but if any changes will take place I’ll update this blog post</p>
<hr />
<div class="pull-right">
<em>Alexander Vershilov</em>
<a href="http://creativecommons.org/licenses/by-nc-sa/3.0"><img src="http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png" /></a>
</div>
<br class="clearfix" />
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'qnikst'; // required: replace example with your forum shortname
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
<footer>
Site generated using <a href="http://jaspervdj.be/hakyll">Hakyll</a> using <a href="http://johnmacfarlane.net/pandoc/">pandoc</a>
</footer>
</div>
</body>
</html>