Allow inspection of object blocking graph
Currently debugging "soft" dead-locks in Haskell concurrent programs (e.g. a thread which is blocked on an MVar
which should get unblocked but isn't due to logic error) is quite difficult. However, we already have many of the pieces in place to improve this situation. Specifically, we already have
threadStatus :: ThreadId -> IO ThreadStatus
data ThreadStatus
= ThreadRunning
| ThreadFinished
| ThreadBlocked BlockReason
| ThreadDied
data BlockReason
= BlockedOnMVar
| BlockedOnBlackHole
| BlockedOnException
| BlockedOnSTM
| BlockedOnForeignCall
| BlockedOnOther
It would be very easy to extend BlockReason
to capture the object on which the thread is blocked.
Furthermore, !2816 (merged) puts in place a mechanism for enumerating a program's threads.
All that remains to make this infrastructure useful for diagnosing deadlocks (and loops) is a means of assigning useful names to heap objects.. Cost centers are one way to achieve this in profiled code. In non-profiled code, IPE information helps for thunks but is unhelpful for primitives like MVar
s.
Sadly, there is no great way of identifying primitives in pure Haskell with the tools that we have today. However, this could be addressed by introducing a new type of primitive object: a finite map keyed on (weak) object references:
-- | A map keyed on object references. Entries keyed on GC'd objects will
-- be automatically dropped.
data WeakMap v
insertWeakMap :: forall (lev :: Levity) (k :: TYPE (BoxedRep lev)).
k -> v -> WeakMap v -> WeakMap v
lookupWeakMap :: forall (lev :: Levity) (k :: TYPE (BoxedRep lev)).
k -> WeakMap v -> Maybe v
labelledObjects :: IORef (WeakMap String)
labelObject :: a -> String -> IO ()
labelObject x lbl = do
atomicModifyIORef' labelledObjects (insertWeakMap w lbl)
getObjectLabel :: a -> Maybe String
getObjectLabel x = lookupWeakMap x labelledObjects
In principle one might be able to do implement something like WeakMap
as an Array# (Weak# v)
with some parts written in Cmm, although efficiency would likely be marginal due to the many Weak#
this would produce. It would be better for this to be a new primitive object with GC support. I believe that V8 has an object similar to this, which they call an ephemeron dictionary.
Perhaps even better than String
names might be:
data ObjectLabel where
ObjectLabel :: (Typeable a, Show a) => a -> ObjectLabel
which would make the labels easier to interpret mechanically.