-- | Modify the configuration while temporarily acquiring
-- the configuration lock. If the configuration lock is held by
-- someone else, nothing is changed and Nothing is returned.
modifyConfigAndReturnWithLock
  :: (TempResState -> ConfigState -> AtomicModifyMonad (a, ConfigState))
     -> State TempResState ()
     -> WConfdMonad (Maybe a)
modifyConfigAndReturnWithLock f tempres = do
  now <- liftIO getClockTime
  dh <- lift . WConfdMonadInt $ ask
  pid <- liftIO getProcessID
  tid <- liftIO myThreadId
  let cid = ClientId { ciIdentifier = ClientOther $ "wconfd-" ++ show tid
                     , ciLockFile = dhLivelock dh
                     , ciPid = pid
                     }
  let modCS ds@(DaemonState { dsTempRes = tr }) =
        mapMOf2
          dsConfigStateL
          (\cs -> liftM (unpackConfigResult now cs)  (f tr cs))
          ds
  maybeDist <- bracket
    (atomicModifyWithLens (dhDaemonState dh) dsLockWaitingL
      $ swap . LW.updateLocks cid [LA.requestExclusive ConfigLock])
    (\(res, _) -> case res of
        Ok s | S.null s -> do
          (_, nfy) <- atomicModifyWithLens (dhDaemonState dh) dsLockWaitingL
                      $ swap . LW.updateLocks cid [LA.requestRelease ConfigLock]
          unless (S.null nfy) . liftIO . void . forkIO $ do
            logDebug . (++) "Locks became available for " . show $ S.toList nfy
            mapM_ (notifyJob . ciPid) $ S.toList nfy
            logDebug "Finished notifying processes"
        _ -> return ())
    (\(res, _) -> case res of
        Ok s | S.null s ->do
          ret <- atomicModifyIORefErrLog (dhDaemonState dh)
                                 (liftM swap . modCS)
          atomicModifyWithLens (dhDaemonState dh) dsTempResL $ runState tempres
          return $ Just ret
        _ -> return Nothing)
  flip (maybe $ return Nothing) maybeDist $ \(val, modified, dist) -> do
    when modified $ do
      logDebug . (++) "Triggering config write; distribution "
        $ if dist then "synchronously" else "asynchronously"
      liftBase . triggerAndWait (Any dist) $ dhSaveConfigWorker dh
      logDebug "Config write finished"
    logDebug "Triggering temporary reservations write"
    liftBase . triggerAndWait_ . dhSaveTempResWorker $ dh
    logDebug "Temporary reservations write finished"
    return $ Just val
modifyConfigAndReturnWithLock
  :: (TempResState -> ConfigState -> AtomicModifyMonad (a, ConfigState))
     -> State TempResState ()
     -> WConfdMonad (Maybe a)
modifyConfigAndReturnWithLock f tempres = do
  now <- liftIO getClockTime
  dh <- lift . WConfdMonadInt $ ask
  pid <- liftIO getProcessID
  tid <- liftIO myThreadId
  let cid = ClientId { ciIdentifier = ClientOther $ "wconfd-" ++ show tid
                     , ciLockFile = dhLivelock dh
                     , ciPid = pid
                     }
  let modCS ds@(DaemonState { dsTempRes = tr }) =
        mapMOf2
          dsConfigStateL
          (\cs -> liftM (unpackConfigResult now cs)  (f tr cs))
          ds
  maybeDist <- bracket
    (atomicModifyWithLens (dhDaemonState dh) dsLockWaitingL
      $ swap . LW.updateLocks cid [LA.requestExclusive ConfigLock])
    (\(res, _) -> case res of
        Ok s | S.null s -> do
          (_, nfy) <- atomicModifyWithLens (dhDaemonState dh) dsLockWaitingL
                      $ swap . LW.updateLocks cid [LA.requestRelease ConfigLock]
          unless (S.null nfy) . liftIO . void . forkIO $ do
            logDebug . (++) "Locks became available for " . show $ S.toList nfy
            mapM_ (notifyJob . ciPid) $ S.toList nfy
            logDebug "Finished notifying processes"
        _ -> return ())
    (\(res, _) -> case res of
        Ok s | S.null s ->do
          ret <- atomicModifyIORefErrLog (dhDaemonState dh)
                                 (liftM swap . modCS)
          atomicModifyWithLens (dhDaemonState dh) dsTempResL $ runState tempres
          return $ Just ret
        _ -> return Nothing)
  flip (maybe $ return Nothing) maybeDist $ \(val, modified, dist) -> do
    when modified $ do
      logDebug . (++) "Triggering config write; distribution "
        $ if dist then "synchronously" else "asynchronously"
      liftBase . triggerAndWait (Any dist) $ dhSaveConfigWorker dh
      logDebug "Config write finished"
    logDebug "Triggering temporary reservations write"
    liftBase . triggerAndWait_ . dhSaveTempResWorker $ dh
    logDebug "Temporary reservations write finished"
    return $ Just val
modifyConfigAndReturnWithLock f tempres = do
  now <- liftIO getClockTime
  dh <- lift . WConfdMonadInt $ ask
  pid <- liftIO getProcessID
  tid <- liftIO myThreadId
  let cid = ClientId { ciIdentifier = ClientOther $ "wconfd-" ++ show tid
                     , ciLockFile = dhLivelock dh
                     , ciPid = pid
                     }
  let modCS ds@(DaemonState { dsTempRes = tr }) =
        mapMOf2
          dsConfigStateL
          (\cs -> liftM (unpackConfigResult now cs)  (f tr cs))
          ds
  maybeDist <- bracket
    (atomicModifyWithLens (dhDaemonState dh) dsLockWaitingL
      $ swap . LW.updateLocks cid [LA.requestExclusive ConfigLock])
    (\(res, _) -> case res of
        Ok s | S.null s -> do
          (_, nfy) <- atomicModifyWithLens (dhDaemonState dh) dsLockWaitingL
                      $ swap . LW.updateLocks cid [LA.requestRelease ConfigLock]
          unless (S.null nfy) . liftIO . void . forkIO $ do
            logDebug . (++) "Locks became available for " . show $ S.toList nfy
            mapM_ (notifyJob . ciPid) $ S.toList nfy
            logDebug "Finished notifying processes"
        _ -> return ())
    (\(res, _) -> case res of
        Ok s | S.null s ->do
          ret <- atomicModifyIORefErrLog (dhDaemonState dh)
                                 (liftM swap . modCS)
          atomicModifyWithLens (dhDaemonState dh) dsTempResL $ runState tempres
          return $ Just ret
        _ -> return Nothing)
  flip (maybe $ return Nothing) maybeDist $ \(val, modified, dist) -> do
    when modified $ do
      logDebug . (++) "Triggering config write; distribution "
        $ if dist then "synchronously" else "asynchronously"
      liftBase . triggerAndWait (Any dist) $ dhSaveConfigWorker dh
      logDebug "Config write finished"
    logDebug "Triggering temporary reservations write"
    liftBase . triggerAndWait_ . dhSaveTempResWorker $ dh
    logDebug "Temporary reservations write finished"
    return $ Just val