Commit f2717b21 authored by Petr Pudlak's avatar Petr Pudlak

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
, getFStatSafe
, needsReload
, watchFile
, watchFileBy
, safeRenameFile
, FilePermissions(..)
, ensurePermissions
) where
import Control.Concurrent
import Control.Exception (try)
import Control.Exception (try, bracket)
import Control.Monad
import Control.Monad.Error
import Data.Char (toUpper, isAlphaNum, isDigit, isSpace)
......@@ -609,53 +610,57 @@ needsReload oldstat path = do
-- 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
-- has a value different than the given one.
watchFileEx :: (Eq a, Eq b) => Integer -> b -> IORef b -> a -> IO a -> IO a
watchFileEx endtime base ref old read_fn = do
watchFileEx :: (Eq b) => Integer -> b -> IORef b -> (a -> Bool) -> IO a -> IO a
watchFileEx endtime base ref check read_fn = do
current <- getCurrentTimeUSec
if current > endtime then read_fn else do
val <- readIORef ref
if val /= base
then do
new <- read_fn
if new /= old then return new else do
if check new then return new else do
logDebug "Observed change not relevant"
threadDelay 100000
watchFileEx endtime val ref old read_fn
else do
watchFileEx endtime val ref check read_fn
else do
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
-- 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
-- of the given method to satisfy a given predicate 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 read_fn = do
watchFileBy :: FilePath -> Int -> (a -> Bool) -> IO a -> IO a
watchFileBy fpath timeout check read_fn = do
current <- getCurrentTimeUSec
let endtime = current + fromIntegral timeout * 1000000
fstat <- getFStatSafe fpath
ref <- newIORef fstat
inotify <- initINotify
let do_watch e = do
logDebug $ "Notified of change in " ++ fpath
++ "; event: " ++ show e
when (e == Ignored)
(addWatch inotify [Modify, Delete] fpath do_watch
>> return ())
fstat' <- getFStatSafe fpath
writeIORef ref fstat'
_ <- addWatch inotify [Modify, Delete] fpath do_watch
newval <- read_fn
if newval /= old
then do
logDebug $ "File " ++ fpath ++ " changed during setup of inotify"
killINotify inotify
return newval
else do
result <- watchFileEx endtime fstat ref old read_fn
killINotify inotify
return result
bracket initINotify killINotify $ \inotify -> do
let do_watch e = do
logDebug $ "Notified of change in " ++ fpath
++ "; event: " ++ show e
when (e == Ignored)
(addWatch inotify [Modify, Delete] fpath do_watch
>> return ())
fstat' <- getFStatSafe fpath
writeIORef ref fstat'
_ <- addWatch inotify [Modify, Delete] fpath do_watch
newval <- read_fn
if check newval
then do
logDebug $ "File " ++ fpath ++ " changed during setup of inotify"
return newval
else watchFileEx endtime fstat ref check read_fn
-- | 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
-- 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
-- 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