I am scratching my head around a memory leak using Control.Distributed.Process.Platform.ManagedProcess.The code exhibiting the behaviour can be seen in leak.hs at https://github.com/alanz/hroq/tree/0cd76a99ce18ece2f8ec5706072dda4df341f779
I have tried stripping out the actual write to disk, and looping over the call to the write directly.
It only manifests itself if the handleCall is used.
Hopefully it is something stupid I have overlooked.
One thing you might like to do is enable template haskell support and generate all your binary instances using `$(derive makeBinary ''<Type>)` instead of rolling them by hand.
handleCall ((\s v -> reply () s) :: State -> String -> Process (ProcessReply State ()))
It still leaks memory, which is reported as PINNED
I did a very quick dive into the relevant code of GenProcess, and followed the rabbit hole until I saw ForeignPtr's .
In the process I realised that this is a very complex piece of code.
My limited knowledge of GHC indicates that ForeignPtr's result in PINNED memory?
Alan
It is stripped down to passing through a String, with a serverDefinition of
handleCall ((\s v -> reply () s) :: State -> String -> Process (ProcessReply State ()))
It still leaks memory, which is reported as PINNED
I did a very quick dive into the relevant code of GenProcess, and followed the rabbit hole until I saw ForeignPtr's .In the process I realised that this is a very complex piece of code.
My limited knowledge of GHC indicates that ForeignPtr's result in PINNED memory?
let x = [] `seq` map (\n -> messageToPayload $ createMessage $ ("bar" ++ (show n)) ) [1..800]let y = [] `seq` map (\m -> payloadToMessage m) xsay $ "messages=" ++ (show (x)) -- Force evaluation of xsay $ "messages=" ++ (show (y)) -- Force evaluation of yThe memory profile shows PINNED memory rising to a peak, then falling symetrically.
So it does get reclaimed. I will run your simple version in the morning, and see what happens.
Alan
Correction: cast does not leak, call does
This result is with sending a String only, so no additional Binary instances.
On 14 Apr 2013, at 12:32, Tim Watson wrote:
> On 14 Apr 2013, at 11:46, AlanKim Zimmerman wrote:
>> Great.
>>
>> I started diving into the code for callAsync and wait, but then my head nearly exploded :)
>>
>
> He he he - yeah it's a nice simple API for the end user, but the insides are a bit complicated. There seems to be a leak here though, and the leak appears to be in the Async code rather than ManagedProcess itself. I'm able to reproduce the leak in pinned byte strings with the following server and client code (which is derived from how ManagedProcess#call works) - taken from https://github.com/haskell-distributed/distributed-process-platform/blob/misc-fixes/regressions/LeakByteStrings.hs:
>
> call :: ProcessId -> String -> Process ()
> call pid msg = do
> asyncRef <- async $ do
> mRef <- monitor pid
> self <- getSelfPid
> send pid (self, msg)
> r <- receiveWait [
> match (\() -> return Nothing)
> , matchIf
> (\(ProcessMonitorNotification ref _ _) -> ref == mRef)
> (\(ProcessMonitorNotification _ _ reason) -> return (Just reason))
> ]
> unmonitor mRef
> case r of
> Nothing -> return ()
> Just err -> die $ "ServerExit (" ++ (show err) ++ ")"
> asyncResult <- wait asyncRef
> case asyncResult of
> (AsyncDone ()) -> return ()
> _ -> die "unexpected async result"
>
> startServer :: Process ProcessId
> startServer = spawnLocal listen
> where listen = do
> receiveWait [
> match (\(pid, _ :: String) -> say "got string" >> send pid ())
> , match (\() -> die "terminating")
> ]
> listen
>
> I've attached the profiling report - I suspect that there's something wrong in the underlying Async implementation, which as you've noticed is a bit complicated. A diagnosis will follow.
>
> Cheers,
> Tim
>
I have been running distributed-process from master on github, (6785222a), and distributed-process-platform 54c350b, which shows the leak.
If I switch to distributed-process 1659f and distributed-process-platform eabd75ef and run my github.com/alanz/hroq 4f1e2f, I get the following, which is what I would hope for. Max memory usage 230k
If I run my original app test against it, it performs as expected. The sawtooth on hroq.app.ps is from dropping buckets from memory to disk as the queue fills.
It will be interesting to see what happens in the actual distributed mode.