Revision 15208e95

b/src/Ganeti/Locking/Allocation.hs
28 28
  , emptyAllocation
29 29
  , OwnerState(..)
30 30
  , listLocks
31
  , LockRequest(..)
32
  , requestExclusive
33
  , requestShared
34
  , requestRelease
35
  , updateLocks
31 36
  ) where
32 37

  
38
import Control.Arrow (second, (***))
39
import Control.Monad
40
import Data.Foldable (for_)
33 41
import qualified Data.Map as M
34 42
import Data.Maybe (fromMaybe)
35 43
import qualified Data.Set as S
36 44

  
45
import Ganeti.BasicTypes
46

  
37 47
{-
38 48

  
39 49
This module is parametric in the type of locks and lock owners.
......
76 86
                 , laOwned = M.empty
77 87
                 }
78 88

  
79
-- | Obtain the set of locks held by a given owner.
80 89
listLocks :: Ord b => b -> LockAllocation a b -> M.Map a OwnerState
81 90
listLocks owner = fromMaybe M.empty . M.lookup owner . laOwned
91

  
92
-- | Data Type describing a change request on a single lock.
93
data LockRequest a = LockRequest { lockAffected :: a
94
                                 , lockRequestType :: Maybe OwnerState
95
                                 }
96
                     deriving (Eq, Show)
97

  
98
-- | Lock request for an exclusive lock.
99
requestExclusive :: a -> LockRequest a
100
requestExclusive lock = LockRequest { lockAffected = lock
101
                                    , lockRequestType = Just OwnExclusive }
102

  
103
-- | Lock request for a shared lock.
104
requestShared :: a -> LockRequest a
105
requestShared lock = LockRequest { lockAffected = lock
106
                                 , lockRequestType = Just OwnShared }
107

  
108
-- | Request to release a lock.
109
requestRelease :: a -> LockRequest a
110
requestRelease lock = LockRequest { lockAffected = lock
111
                                  , lockRequestType = Nothing }
112

  
113
-- | Internal function to update the state according to a single
114
-- lock request, assuming all prerequisites are met.
115
updateLock :: (Ord a, Ord b)
116
           => b
117
           -> LockAllocation a b -> LockRequest a -> LockAllocation a b
118
updateLock owner state (LockRequest lock (Just OwnExclusive)) =
119
  let locks' = M.insert lock (Exclusive owner) $ laLocks state
120
      ownersLocks' = M.insert lock OwnExclusive $ listLocks owner state
121
      owned' = M.insert owner ownersLocks' $ laOwned state
122
  in state { laLocks = locks', laOwned = owned' }
123
updateLock owner state (LockRequest lock (Just OwnShared)) =
124
  let ownersLocks' = M.insert lock OwnShared $ listLocks owner state
125
      owned' = M.insert owner ownersLocks' $ laOwned state
126
      locks = laLocks state
127
      lockState' = case M.lookup lock locks of
128
        Just (Shared s) -> Shared (S.insert owner s)
129
        _ -> Shared $ S.singleton owner
130
      locks' = M.insert lock lockState' locks
131
  in state { laLocks = locks', laOwned = owned' }
132
updateLock owner state (LockRequest lock Nothing) =
133
  let ownersLocks' = M.delete lock $ listLocks owner state
134
      owned = laOwned state
135
      owned' = if M.null ownersLocks'
136
                 then M.delete owner owned
137
                 else M.insert owner ownersLocks' owned
138
      locks = laLocks state
139
      lockRemoved = M.delete lock locks
140
      locks' = case M.lookup lock locks of
141
                 Nothing -> locks
142
                 Just (Exclusive x) ->
143
                   if x == owner then lockRemoved else locks
144
                 Just (Shared s)
145
                   -> let s' = S.delete owner s
146
                      in if S.null s'
147
                        then lockRemoved
148
                        else M.insert lock (Shared s') locks
149
  in state { laLocks = locks', laOwned = owned' }
150

  
151
-- | Update the locks of an owner according to the given request. Return
152
-- the pair of the new state and the result of the operation, which is the
153
-- the set of owners on which the operation was blocked on. so an empty set is
154
-- success, and the state is updated if, and only if, the returned set is emtpy.
155
-- In that way, it can be used in atomicModifyIORef.
156
updateLocks :: (Ord a, Show a, Ord b)
157
               => b
158
               -> [LockRequest a]
159
               -> LockAllocation a b -> (LockAllocation a b, Result (S.Set b))
160
updateLocks owner reqs state = genericResult ((,) state . Bad) (second Ok) $ do
161
  runListHead (return ())
162
              (fail . (++) "Inconsitent requests for lock " . show) $ do
163
      r <- reqs
164
      r' <- reqs
165
      guard $ r /= r'
166
      guard $ lockAffected r == lockAffected r'
167
      return $ lockAffected r
168
  let current = listLocks owner state
169
  unless (M.null current) $ do
170
    let (highest, _) = M.findMax current
171
        notHolding = not
172
                     . any (uncurry (==) . ((M.lookup `flip` current) *** Just))
173
        orderViolation l = fail $ "Order violation: requesting " ++ show l
174
                                   ++ " while holding " ++ show highest
175
    for_ reqs $ \req -> case req of
176
      LockRequest lock (Just OwnExclusive)
177
        | lock < highest && notHolding [ (lock, OwnExclusive) ]
178
        -> orderViolation lock
179
      LockRequest lock (Just OwnShared)
180
        | lock < highest && notHolding [ (lock, OwnExclusive)
181
                                       , (lock, OwnExclusive)]
182
        -> orderViolation lock
183
      _ -> Ok ()
184
  let blockedOn (LockRequest  _ Nothing) = S.empty
185
      blockedOn (LockRequest lock (Just OwnExclusive)) =
186
        case M.lookup lock (laLocks state) of
187
          Just (Exclusive x) -> S.singleton x
188
          Just (Shared xs) -> xs
189
          _ -> S.empty
190
      blockedOn (LockRequest lock (Just OwnShared)) =
191
        case M.lookup lock (laLocks state) of
192
          Just (Exclusive x) -> S.singleton x
193
          _ -> S.empty
194
  let blocked = S.delete owner . S.unions $ map blockedOn reqs
195
  let state' = foldl (updateLock owner) state reqs
196
  return (if S.null blocked then state' else state, blocked)

Also available in: Unified diff