What is the right way to clean up resources using ResourceT?

I played with the conduit-extra UNIX package, which basically makes it easy to create a server using UNIX domain sockets, in particular using runUnixServer funciton .

The problem is that after the function exists, it does not clear the socket file, which means that it must be cleaned manually. Here is a simple example that basically creates an echo server.

 main :: IO () main = do let settings = serverSettings "foobar.sock" runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad)) 

I searched Google a bit and found that the right way to handle resources here is to use resourcet . Although the problem is that most of the APIs in the resources expect me to runUnixSever this resource myself, which is not the case with runUnixSever , which does not return anyhting.

At first I decided to use register to register a function that deletes the file, e.g.

 main :: IO () main = runResourceT $ do register $ removeLink "foobar.sock" let settings = serverSettings "foobar.sock" liftIO $ runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad)) 

There is a problem with this approach, although at least the documentation for allocate says:

This is almost identical to invoking the distribution and registration of the release action, but it correctly handles the masking of asynchronous exceptions.

Does this mean that register by itself does not handle asynchronous exceptions? If so, it could be a problem when one of the handlers generated by runUnixServer (docs say it spawns a thread for each client) causes an error?

The third and final solution I came across is to use allocate to make sure that asynchronous exceptions are handled correctly (I'm not sure if this is really necessary in this case).

 main :: IO () main = runResourceT $ do allocate (return 1) (const $ removeLink "foobar.sock") let settings = serverSettings "foobar.sock" liftIO $ runUnixServer settings (\ad -> (appSource ad) $$ (appSink ad)) 

But is this really the best solution? Since I am creating a value that I will never use (return 1) , and then using the const function to ignore that value in the finalizer.

+7
resources haskell sockets conduit
source share
1 answer

Before addressing the resourcet question:

  • resourcet is not required in this case. You can simply use the finally function for something like this, for example. runUnixServer settings (\ad -> ...) finally removeLink "foobar.sock" .
  • This seems like problematic behavior. The common pattern in conduit is that if you allocate a resource, you are responsible for cleaning it. I did not write unix socket code, so the author may have a reason to do it differently. But it’s worth opening an error report.

However, your initial code with register is fine. The only problem I see is the exception thrown before foobar.sock , although my finally solution is also vulnerable to this.

The vs register allocation comment is related to the code, which is as follows:

 handle <- openFile fp ReadMode register $ hClose handle 

This code is vulnerable to avoiding an asynchronous call between openFile and register calls. Since you are not allocating such a resource, register is fine.

+9
source share

All Articles