{- |
   Module     : Database.HDBC.DriverUtils
   Copyright  : Copyright (C) 2006 John Goerzen
   License    : BSD3

   Maintainer : John Goerzen <jgoerzen@complete.org>
   Stability  : provisional
   Portability: portable

Utilities for database backend drivers.

Please note: this module is intended for authors of database driver libraries
only.  Authors of applications using HDBC should use 'Database.HDBC'
exclusively.

Written by John Goerzen, jgoerzen\@complete.org
-}

module Database.HDBC.DriverUtils (
                                  ChildList,
                                  closeAllChildren,
                                  addChild
                                 )

where
import Control.Concurrent.MVar
import System.Mem.Weak
import Database.HDBC.Types
import Control.Monad

type ChildList = MVar [Weak Statement]

{- | Close all children.  Intended to be called by the 'disconnect' function
in 'Connection'. 

There may be a potential race condition wherein a call to newSth at the same
time as a call to this function may result in the new child not being closed.
-}
closeAllChildren :: ChildList -> IO ()
closeAllChildren :: ChildList -> IO ()
closeAllChildren ChildList
mcl = 
    do children <- ChildList -> IO [Weak Statement]
forall a. MVar a -> IO a
readMVar ChildList
mcl
       mapM_ closefunc children
    where closefunc :: Weak Statement -> IO ()
closefunc Weak Statement
child =
              do c <- Weak Statement -> IO (Maybe Statement)
forall v. Weak v -> IO (Maybe v)
deRefWeak Weak Statement
child
                 case c of
                   Maybe Statement
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
                   Just Statement
x -> Statement -> IO ()
finish Statement
x

{- | Adds a new child to the existing list.  Also takes care of registering
a finalizer for it, to remove it from the list when possible. -}
addChild :: ChildList -> Statement -> IO ()
addChild :: ChildList -> Statement -> IO ()
addChild ChildList
mcl Statement
stmt =
    do weakptr <- Statement -> Maybe (IO ()) -> IO (Weak Statement)
forall k. k -> Maybe (IO ()) -> IO (Weak k)
mkWeakPtr Statement
stmt (IO () -> Maybe (IO ())
forall a. a -> Maybe a
Just (ChildList -> IO ()
childFinalizer ChildList
mcl))
       modifyMVar_ mcl (\[Weak Statement]
l -> [Weak Statement] -> IO [Weak Statement]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Weak Statement
weakptr Weak Statement -> [Weak Statement] -> [Weak Statement]
forall a. a -> [a] -> [a]
: [Weak Statement]
l))

{- | The general finalizer for a child.

It is simply a filter that removes any finalized weak pointers from the parent.

If the MVar is locked at the start, does nothing to avoid deadlock.  Future
runs would probably catch it anyway. -}
childFinalizer :: ChildList -> IO ()
childFinalizer :: ChildList -> IO ()
childFinalizer ChildList
mcl = 
    do c <- ChildList -> IO (Maybe [Weak Statement])
forall a. MVar a -> IO (Maybe a)
tryTakeMVar ChildList
mcl
       case c of
         Maybe [Weak Statement]
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
         Just [Weak Statement]
cl ->
             do newlist <- (Weak Statement -> IO Bool)
-> [Weak Statement] -> IO [Weak Statement]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM Weak Statement -> IO Bool
forall {v}. Weak v -> IO Bool
filterfunc [Weak Statement]
cl
                putMVar mcl newlist
    where filterfunc :: Weak v -> IO Bool
filterfunc Weak v
c =
              do dc <- Weak v -> IO (Maybe v)
forall v. Weak v -> IO (Maybe v)
deRefWeak Weak v
c
                 case dc of
                   Maybe v
Nothing -> Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                   Just v
_ -> Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True