[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-dev] PRELIMINARY: [PATCH 3/3] Add the 'FlexibleInstances' extension.



Iâm attaching three patches.  For an explanation regarding the
âFlexibleInstancesâ extension see [1,2].

As always, Iâve only fixed critical things.  Deprecation warnings can
wait.

Perhaps, Iâll combine all similar patches (like those that deal with
exceptions) later.

Iâve noticed that the author decided to use âDynamicâ-related functions
to raise many (all?) exceptions.  If you raise an exception of type
âDynamicâ, you wonât get a meaningful message.  Consider the following:

{-# LANGUAGE DeriveDataTypeable #-}

import qualified Control.Exception as E
import Data.Dynamic (toDyn)
import Data.Typeable (Typeable)

-- | An exception related to links or monitors.
data LinkException = NonexistentThread -- ^
deriving (Eq, Typeable)

instance Show LinkException where
show NonexistentThread = "Attempt to link to nonexistent thread"

test1 = E.throw . toDyn $ NonexistentThread

instance E.Exception LinkException where

test2 = E.throw NonexistentThread

In GHCi:

*Main> test1
*** Exception: <<LinkException>>
*Main> test2
*** Exception: Attempt to link to nonexistent thread

Note that the first argument of âE.throwâ must be an instance of
âE.Exceptionâ:

E.throw :: E.Exception e => e -> a

âLinkExceptionâ is not an instance of âE.Exceptionâ in
âTorDNSEL.Control.Concurrent.Link.Internalsâ.  It should be easy to
change that.

Later, Iâd also like to inspect âwithLinksDoâ, âlinkTogetherâ, and
replace â$â with â.â in a couple of places.

The previous set of patches is here [3].

[1] http://www.haskell.org/haskellwiki/List_instance
[2] http://www.haskell.org/ghc/docs/6.8-latest/html/users_guide/type-class-extensions.html#instance-rules
[3] https://lists.torproject.org/pipermail/tor-dev/2013-July/005157.html

From e7a064af8ff914a54d9c0eaf1ef7c17c84ed621e Mon Sep 17 00:00:00 2001
From: Nikita Karetnikov <nikita@xxxxxxxxxxxxxx>
Date: Sat, 3 Aug 2013 12:53:52 +0000
Subject: [PATCH 1/3] Replace 'TorDNSEL.Compat.Exception' with
 'Control.Exception'.

---
 src/TorDNSEL/Control/Concurrent/Link/Internals.hs |   89 ++++++++++++---------
 src/TorDNSEL/Control/Concurrent/Util.hs           |    6 +-
 2 files changed, 54 insertions(+), 41 deletions(-)

diff --git a/src/TorDNSEL/Control/Concurrent/Link/Internals.hs b/src/TorDNSEL/Control/Concurrent/Link/Internals.hs
index 8f8988e..14b2248 100644
--- a/src/TorDNSEL/Control/Concurrent/Link/Internals.hs
+++ b/src/TorDNSEL/Control/Concurrent/Link/Internals.hs
@@ -28,7 +28,8 @@ module TorDNSEL.Control.Concurrent.Link.Internals where
 import qualified Control.Concurrent as C
 import Control.Concurrent.MVar
   (MVar, newMVar, withMVar, modifyMVar, modifyMVar_)
-import qualified TorDNSEL.Compat.Exception as E
+import GHC.Conc.Sync (setUncaughtExceptionHandler)
+import qualified Control.Exception as E
 import Control.Monad (unless)
 import qualified Data.Foldable as F
 import qualified Data.Map as M
@@ -38,7 +39,7 @@ import Data.List (nub)
 import Data.Unique (Unique, newUnique)
 import System.IO (hPutStrLn, hFlush, stderr)
 import System.IO.Unsafe (unsafePerformIO)
-
+import System.Exit (ExitCode)
 import TorDNSEL.Util
 
 -- | An abstract type representing a handle to a linkable thread. Holding a
@@ -75,11 +76,17 @@ threadMap :: MVar ThreadMap
 {-# NOINLINE threadMap #-}
 threadMap = unsafePerformIO . newMVar $ ThreadMap M.empty M.empty
 
+-- | A predicate that matches assertions.
+assertions :: E.SomeException -> Maybe String
+assertions e = case E.fromException e :: Maybe E.AssertionFailed of
+  Nothing -> Nothing
+  Just e' -> Just (show e')
+
 -- | Assert various invariants of the global link and monitor state, printing a
 -- message to stdout if any assertions fail.
 assertThreadMap :: ThreadMap -> IO ()
 assertThreadMap tm =
-  E.handleJust E.assertions (putStr . ("assertThreadMap: " ++)) $
+  E.handleJust assertions (putStr . ("assertThreadMap: " ++)) $
     E.assert (M.size (ids tm) > 0) $
     E.assert (M.size (ids tm) == M.size (state tm)) $
     E.assert (M.elems (ids tm) == nub (M.elems (ids tm))) $
@@ -106,31 +113,37 @@ data ExitSignal = ExitSignal !ThreadId !ExitReason
 -- | Extract the 'ExitReason' from an 'ExitSignal' contained within a
 -- dynamically-typed exception. If the exception doesn't contain an
 -- 'ExitSignal', tag it with 'Just'.
-extractReason :: E.Exception -> ExitReason
-extractReason (E.DynException dyn)
-  | Just (ExitSignal _ e) <- fromDynamic dyn = e
-extractReason e                              = Just e
-
--- | Extract an exit signal from an 'E.Exception' if it has the right type.
-fromExitSignal :: Typeable a => E.Exception -> Maybe (ThreadId, a)
-fromExitSignal (E.DynException d)
-  | Just (ExitSignal tid (Just (E.DynException d'))) <- fromDynamic d
+extractReason :: E.SomeException -> ExitReason
+extractReason e
+  | Just dyn <- E.fromException e :: Maybe Dynamic
+  , Just (ExitSignal _ e') <- fromDynamic dyn
+  = e'
+  | otherwise = Just e
+
+-- | Extract an exit signal from 'E.SomeException' if it has the right
+-- type.
+fromExitSignal :: Typeable a => E.SomeException -> Maybe (ThreadId, a)
+fromExitSignal e
+  | Just d <- E.fromException e :: Maybe Dynamic
+  , Just (ExitSignal tid (Just e')) <- fromDynamic d
+  , Just d' <- E.fromException e' :: Maybe Dynamic
   = (,) tid `fmap` fromDynamic d'
-fromExitSignal _ = Nothing
+  | otherwise = Nothing
 
 -- | The default action used to signal a thread. Abnormal 'ExitReason's are
 -- sent to the thread and normal exits are ignored.
 defaultSignal :: C.ThreadId -> ThreadId -> ExitReason -> IO ()
-defaultSignal dst src e@(Just _) = E.throwDynTo dst $ ExitSignal src e
+defaultSignal dst src e@(Just _) =
+  E.throwTo dst $ E.toException $ toDyn $ ExitSignal src e
 defaultSignal _   _      Nothing = return ()
 
 -- | Initialize the state supporting links and monitors. Use the given function
 -- to display an uncaught exception. It is an error to call this function
 -- outside the main thread, or to call any other functions in this module
 -- outside this function.
-withLinksDo :: (E.Exception -> String) -> IO a -> IO ()
-withLinksDo showE io = E.block $ do
-  E.setUncaughtExceptionHandler . const . return $ ()
+withLinksDo :: (E.SomeException -> String) -> IO a -> IO ()
+withLinksDo showE io = E.mask $ \restore -> do
+  setUncaughtExceptionHandler . const . return $ ()
   main <- C.myThreadId
   mainId <- Tid `fmap` newUnique
   let initialState = ThreadState
@@ -140,21 +153,22 @@ withLinksDo showE io = E.block $ do
         , monitors  = M.empty
         , ownedMons = S.empty }
   modifyMVar_ threadMap $ \tm ->
-    E.assert (M.size (ids tm) == 0) $
-    E.assert (M.size (state tm) == 0) $
+    E.assert (M.null (ids tm)) $
+    E.assert (M.null (state tm)) $
     return $! initialState `seq`
       tm { ids   = M.insert mainId main (ids tm)
          , state = M.insert main initialState (state tm) }
   -- Don't bother propagating signals from the main thread
   -- since it's about to exit.
-  (E.unblock io >> return ()) `E.catch` \e ->
+  (restore io >> return ()) `E.catch` \e ->
     case extractReason e of
-      Nothing                     -> return ()
-      Just e'@(E.ExitException _) -> E.throwIO e'
-      Just e' -> do
-        hPutStrLn stderr ("*** Exception: " ++ showE e')
-        hFlush stderr
-        E.throwIO e'
+      Nothing -> return ()
+      Just e' -> case E.fromException e' :: Maybe ExitCode of
+        Just _  -> E.throwIO e'
+        Nothing -> do
+          hPutStrLn stderr ("*** Exception: " ++ showE e')
+          hFlush stderr
+          E.throwIO e'
 
 -- | Evaluate the given 'IO' action in a new thread, returning its 'ThreadId'.
 forkIO :: IO a -> IO ThreadId
@@ -216,7 +230,7 @@ forkLinkIO' shouldLink io = E.block $ do
   return childId
   where
     forkHandler = C.forkIO . ignore . (>> return ()) . E.block
-    ignore = E.handle . const . return $ ()
+    ignore      = E.handle (const . return $ () :: E.SomeException -> IO ())
 
 -- | Establish a bidirectional link between the calling thread and a given
 -- thread. If either thread terminates, an exit signal will be sent to the other
@@ -234,8 +248,8 @@ linkThread tid = do
                        in tm' `seq` return (tm', Nothing)
       Nothing ->
         let s = state tm M.! me
-        in return (tm, Just . signal s tid . Just . E.DynException .
-                         toDyn $ NonexistentThread)
+        in return (tm, Just . signal s tid . Just . E.toException
+                       . toDyn $ NonexistentThread)
   whenJust mbSignalSelf id
   where linkTogether x y = (x `linkTo` y) . (y `linkTo` x)
 
@@ -261,7 +275,7 @@ data Monitor = Monitor !ThreadId !Unique
 
 -- | The reason a thread was terminated. @Nothing@ means the thread exited
 -- normally. @Just exception@ contains the reason for an abnormal exit.
-type ExitReason = Maybe E.Exception
+type ExitReason = Maybe E.SomeException
 
 -- | Start monitoring the given thread, invoking an 'IO' action with the
 -- 'ExitReason' when the thread dies. Return a handle to the monitor, which can
@@ -285,7 +299,7 @@ monitorThread tid notify = do
                                  adjust' (addOwned tid') me $ state tm }
         in tm' `seq` return (tm', True)
   unless exists $
-    notify . Just . E.DynException . toDyn $ NonexistentThread
+    notify . Just . E.toException . toDyn $ NonexistentThread
   return mon
 
 -- | Cancel a monitor, if it is currently active.
@@ -311,7 +325,7 @@ withMonitor tid notify =
 
 -- | Terminate the calling thread with the given 'ExitReason'.
 exit :: ExitReason -> IO a
-exit e = E.throwDyn . flip ExitSignal e =<< myThreadId
+exit e = E.throw . toDyn . flip ExitSignal e =<< myThreadId
 
 -- | Send an exit signal with an 'ExitReason' to a thread. If the 'ExitReason'
 -- is 'Nothing', the signal will be ignored unless the target thread is trapping
@@ -325,7 +339,7 @@ throwTo tid e = do
     let me' = ident (state tm M.! me)
     in if tid == me'
          -- special case: an exception thrown to oneself is untrappable
-         then E.throwDyn $ ExitSignal me' e
+         then E.throw . toDyn $ ExitSignal me' e
          else return $ do tid' <- M.lookup tid (ids tm)
                           return $ signal (state tm M.! tid') me'
   -- since signal can block, we don't want to hold a lock on threadMap
@@ -333,7 +347,7 @@ throwTo tid e = do
 
 -- | A variant of 'throwTo' for dynamically typed 'ExitReason's.
 throwDynTo :: Typeable a => ThreadId -> a -> IO ()
-throwDynTo tid = throwTo tid . Just . E.DynException . toDyn
+throwDynTo tid = throwTo tid . Just . E.toException . toDyn
 
 -- | Send an untrappable exit signal to a thread, if it exists.
 killThread :: ThreadId -> IO ()
@@ -341,9 +355,8 @@ killThread tid = do
   me <- C.myThreadId
   mbSignal <- withMVar threadMap $ \tm -> return $ do
     tid' <- M.lookup tid (ids tm)
-    return .
-      E.throwDynTo tid' $ ExitSignal (ident (state tm M.! me))
-                                     (Just (E.AsyncException E.ThreadKilled))
+    return . E.throwTo tid' . toDyn . ExitSignal (ident (state tm M.! me))
+      . Just $ E.toException E.ThreadKilled
   whenJust mbSignal id
 
 -- | Redirect exit signals destined for the calling thread to the given 'IO'
@@ -362,7 +375,7 @@ unsetTrapExit :: IO ()
 unsetTrapExit = setTrapExit . defaultSignal =<< C.myThreadId
 
 -- | An exception related to links or monitors.
-data LinkException = NonexistentThread -- ^ 
+data LinkException = NonexistentThread -- ^
   deriving (Eq, Typeable)
 
 instance Show LinkException where
diff --git a/src/TorDNSEL/Control/Concurrent/Util.hs b/src/TorDNSEL/Control/Concurrent/Util.hs
index b502f4b..395a7fd 100644
--- a/src/TorDNSEL/Control/Concurrent/Util.hs
+++ b/src/TorDNSEL/Control/Concurrent/Util.hs
@@ -12,9 +12,9 @@
 -----------------------------------------------------------------------------
 module TorDNSEL.Control.Concurrent.Util where
 
-import qualified TorDNSEL.Compat.Exception as E
+import qualified Control.Exception as E
 import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar, tryPutMVar)
-import Data.Dynamic (Dynamic)
+import Data.Dynamic (Dynamic, toDyn)
 import Data.Maybe (isJust)
 
 import TorDNSEL.Control.Concurrent.Link
@@ -67,7 +67,7 @@ call sendMsg tid = do
     sendMsg $ putResponse . Right
     response <- takeMVar mv
     case response of
-      Left Nothing  -> E.throwDyn NonexistentThread
+      Left Nothing  -> E.throw . toDyn $ NonexistentThread
       Left (Just e) -> E.throwIO e
       Right r       -> return r
 
-- 
1.7.9.5

From 5fee61b3961d078c30e69fe70404f08b38690fcb Mon Sep 17 00:00:00 2001
From: Nikita Karetnikov <nikita@xxxxxxxxxxxxxx>
Date: Sat, 3 Aug 2013 13:05:17 +0000
Subject: [PATCH 2/3] Import the 'CInt' constructor properly.

---
 src/TorDNSEL/Log/Internals.hsc |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/TorDNSEL/Log/Internals.hsc b/src/TorDNSEL/Log/Internals.hsc
index 5e7854e..53f5cba 100644
--- a/src/TorDNSEL/Log/Internals.hsc
+++ b/src/TorDNSEL/Log/Internals.hsc
@@ -33,7 +33,7 @@ import Data.Bits ((.|.))
 import qualified Data.ByteString.Char8 as B
 import Data.List (foldl')
 import Data.Time (UTCTime, getCurrentTime)
-import Foreign.C (CString, CInt, withCString)
+import Foreign.C (CString, CInt(..), withCString)
 import System.IO
   (Handle, stdout, stderr, openFile, IOMode(AppendMode), hFlush, hClose)
 import System.IO.Unsafe (unsafePerformIO)
-- 
1.7.9.5

From 32f473eb33f6e52a384ca8164c6b4bd94df50994 Mon Sep 17 00:00:00 2001
From: Nikita Karetnikov <nikita@xxxxxxxxxxxxxx>
Date: Sat, 3 Aug 2013 13:20:10 +0000
Subject: [PATCH 3/3] Add the 'FlexibleInstances' extension.

---
 src/TorDNSEL/Directory/Internals.hs |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/TorDNSEL/Directory/Internals.hs b/src/TorDNSEL/Directory/Internals.hs
index ace1f68..f6dacfe 100644
--- a/src/TorDNSEL/Directory/Internals.hs
+++ b/src/TorDNSEL/Directory/Internals.hs
@@ -1,4 +1,5 @@
-{-# LANGUAGE PatternGuards, TypeSynonymInstances, FlexibleContexts #-}
+{-# LANGUAGE PatternGuards, TypeSynonymInstances, FlexibleContexts,
+             FlexibleInstances #-}
 
 -----------------------------------------------------------------------------
 -- |
-- 
1.7.9.5

Attachment: pgpfoLxeDNrTX.pgp
Description: PGP signature

_______________________________________________
tor-dev mailing list
tor-dev@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-dev