Commit f2717b21 authored by Petr Pudlak's avatar Petr Pudlak
Browse files

Enhance watchFile in Ganeti.Utils



The functionality is kept the same, but instead of comparing for
equality, a more general version based on a predicate is added.
This allows to base the condition on only a part of the output.

In addition, 'bracket' is added so that inotify data structure is
properly cleaned up even if the inner IO action throws an exception.
Signed-off-by: default avatarPetr Pudlak <pudlak@google.com>
Reviewed-by: default avatarKlaus Aehlig <aehlig@google.com>
parent 633d59ae
...@@ -75,13 +75,14 @@ module Ganeti.Utils ...@@ -75,13 +75,14 @@ module Ganeti.Utils
, getFStatSafe , getFStatSafe
, needsReload , needsReload
, watchFile , watchFile
, watchFileBy
, safeRenameFile , safeRenameFile
, FilePermissions(..) , FilePermissions(..)
, ensurePermissions , ensurePermissions
) where ) where
import Control.Concurrent import Control.Concurrent
import Control.Exception (try) import Control.Exception (try, bracket)
import Control.Monad import Control.Monad
import Control.Monad.Error import Control.Monad.Error
import Data.Char (toUpper, isAlphaNum, isDigit, isSpace) import Data.Char (toUpper, isAlphaNum, isDigit, isSpace)
...@@ -609,34 +610,34 @@ needsReload oldstat path = do ...@@ -609,34 +610,34 @@ needsReload oldstat path = do
-- for the output of a given method to change and return the new value; -- for the output of a given method to change and return the new value;
-- make use of the promise that the output only changes if the reference -- make use of the promise that the output only changes if the reference
-- has a value different than the given one. -- has a value different than the given one.
watchFileEx :: (Eq a, Eq b) => Integer -> b -> IORef b -> a -> IO a -> IO a watchFileEx :: (Eq b) => Integer -> b -> IORef b -> (a -> Bool) -> IO a -> IO a
watchFileEx endtime base ref old read_fn = do watchFileEx endtime base ref check read_fn = do
current <- getCurrentTimeUSec current <- getCurrentTimeUSec
if current > endtime then read_fn else do if current > endtime then read_fn else do
val <- readIORef ref val <- readIORef ref
if val /= base if val /= base
then do then do
new <- read_fn new <- read_fn
if new /= old then return new else do if check new then return new else do
logDebug "Observed change not relevant" logDebug "Observed change not relevant"
threadDelay 100000 threadDelay 100000
watchFileEx endtime val ref old read_fn watchFileEx endtime val ref check read_fn
else do else do
threadDelay 100000 threadDelay 100000
watchFileEx endtime base ref old read_fn watchFileEx endtime base ref check read_fn
-- | Within the given timeout (in seconds), wait for for the output -- | Within the given timeout (in seconds), wait for for the output
-- of the given method to change and return the new value; make use of -- of the given method to satisfy a given predicate and return the new value;
-- the promise that the method will only change its value, if -- make use of the promise that the method will only change its value, if
-- the given file changes on disk. If the file does not exist on disk, return -- the given file changes on disk. If the file does not exist on disk, return
-- immediately. -- immediately.
watchFile :: Eq a => FilePath -> Int -> a -> IO a -> IO a watchFileBy :: FilePath -> Int -> (a -> Bool) -> IO a -> IO a
watchFile fpath timeout old read_fn = do watchFileBy fpath timeout check read_fn = do
current <- getCurrentTimeUSec current <- getCurrentTimeUSec
let endtime = current + fromIntegral timeout * 1000000 let endtime = current + fromIntegral timeout * 1000000
fstat <- getFStatSafe fpath fstat <- getFStatSafe fpath
ref <- newIORef fstat ref <- newIORef fstat
inotify <- initINotify bracket initINotify killINotify $ \inotify -> do
let do_watch e = do let do_watch e = do
logDebug $ "Notified of change in " ++ fpath logDebug $ "Notified of change in " ++ fpath
++ "; event: " ++ show e ++ "; event: " ++ show e
...@@ -647,15 +648,19 @@ watchFile fpath timeout old read_fn = do ...@@ -647,15 +648,19 @@ watchFile fpath timeout old read_fn = do
writeIORef ref fstat' writeIORef ref fstat'
_ <- addWatch inotify [Modify, Delete] fpath do_watch _ <- addWatch inotify [Modify, Delete] fpath do_watch
newval <- read_fn newval <- read_fn
if newval /= old if check newval
then do then do
logDebug $ "File " ++ fpath ++ " changed during setup of inotify" logDebug $ "File " ++ fpath ++ " changed during setup of inotify"
killINotify inotify
return newval return newval
else do else watchFileEx endtime fstat ref check read_fn
result <- watchFileEx endtime fstat ref old read_fn
killINotify inotify -- | Within the given timeout (in seconds), wait for for the output
return result -- of the given method to change and return the new value; make use of
-- the promise that the method will only change its value, if
-- the given file changes on disk. If the file does not exist on disk, return
-- immediately.
watchFile :: Eq a => FilePath -> Int -> a -> IO a -> IO a
watchFile fpath timeout old = watchFileBy fpath timeout (/= old)
-- | Type describing ownership and permissions of newly generated -- | Type describing ownership and permissions of newly generated
-- directories and files. All parameters are optional, with nothing -- directories and files. All parameters are optional, with nothing
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment