Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Locking / Allocation.hs @ 71dc39a1

History | View | Annotate | Download (15.5 kB)

1 c2b8d366 Klaus Aehlig
{-| Implementation of lock allocation.
2 c2b8d366 Klaus Aehlig
3 c2b8d366 Klaus Aehlig
-}
4 c2b8d366 Klaus Aehlig
5 c2b8d366 Klaus Aehlig
{-
6 c2b8d366 Klaus Aehlig
7 c2b8d366 Klaus Aehlig
Copyright (C) 2014 Google Inc.
8 c2b8d366 Klaus Aehlig
9 c2b8d366 Klaus Aehlig
This program is free software; you can redistribute it and/or modify
10 c2b8d366 Klaus Aehlig
it under the terms of the GNU General Public License as published by
11 c2b8d366 Klaus Aehlig
the Free Software Foundation; either version 2 of the License, or
12 c2b8d366 Klaus Aehlig
(at your option) any later version.
13 c2b8d366 Klaus Aehlig
14 c2b8d366 Klaus Aehlig
This program is distributed in the hope that it will be useful, but
15 c2b8d366 Klaus Aehlig
WITHOUT ANY WARRANTY; without even the implied warranty of
16 c2b8d366 Klaus Aehlig
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 c2b8d366 Klaus Aehlig
General Public License for more details.
18 c2b8d366 Klaus Aehlig
19 c2b8d366 Klaus Aehlig
You should have received a copy of the GNU General Public License
20 c2b8d366 Klaus Aehlig
along with this program; if not, write to the Free Software
21 c2b8d366 Klaus Aehlig
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 c2b8d366 Klaus Aehlig
02110-1301, USA.
23 c2b8d366 Klaus Aehlig
24 c2b8d366 Klaus Aehlig
-}
25 c2b8d366 Klaus Aehlig
26 c2b8d366 Klaus Aehlig
module Ganeti.Locking.Allocation
27 c2b8d366 Klaus Aehlig
  ( LockAllocation
28 c2b8d366 Klaus Aehlig
  , emptyAllocation
29 c2b8d366 Klaus Aehlig
  , OwnerState(..)
30 c2b8d366 Klaus Aehlig
  , listLocks
31 15208e95 Klaus Aehlig
  , LockRequest(..)
32 15208e95 Klaus Aehlig
  , requestExclusive
33 15208e95 Klaus Aehlig
  , requestShared
34 15208e95 Klaus Aehlig
  , requestRelease
35 15208e95 Klaus Aehlig
  , updateLocks
36 80004e70 Klaus Aehlig
  , freeLocks
37 d8216f2d Klaus Aehlig
  , intersectLocks
38 4b217f68 Klaus Aehlig
  , opportunisticLockUnion
39 c2b8d366 Klaus Aehlig
  ) where
40 c2b8d366 Klaus Aehlig
41 71dc39a1 Klaus Aehlig
import Control.Applicative (liftA2)
42 15208e95 Klaus Aehlig
import Control.Arrow (second, (***))
43 15208e95 Klaus Aehlig
import Control.Monad
44 381889dc Klaus Aehlig
import Data.Foldable (for_, find)
45 4b217f68 Klaus Aehlig
import Data.List (sort)
46 c2b8d366 Klaus Aehlig
import qualified Data.Map as M
47 c2b8d366 Klaus Aehlig
import Data.Maybe (fromMaybe)
48 c2b8d366 Klaus Aehlig
import qualified Data.Set as S
49 71dc39a1 Klaus Aehlig
import qualified Text.JSON as J
50 c2b8d366 Klaus Aehlig
51 15208e95 Klaus Aehlig
import Ganeti.BasicTypes
52 71dc39a1 Klaus Aehlig
import Ganeti.JSON (toArray)
53 381889dc Klaus Aehlig
import Ganeti.Locking.Types
54 15208e95 Klaus Aehlig
55 c2b8d366 Klaus Aehlig
{-
56 c2b8d366 Klaus Aehlig
57 c2b8d366 Klaus Aehlig
This module is parametric in the type of locks and lock owners.
58 c2b8d366 Klaus Aehlig
While we only state minimal requirements for the types, we will
59 c2b8d366 Klaus Aehlig
consistently use the type variable 'a' for the type of locks and
60 c2b8d366 Klaus Aehlig
the variable 'b' for the type of the lock owners throughout this
61 c2b8d366 Klaus Aehlig
module.
62 c2b8d366 Klaus Aehlig
63 c2b8d366 Klaus Aehlig
-}
64 c2b8d366 Klaus Aehlig
65 c2b8d366 Klaus Aehlig
-- | Data type describing the way a lock can be owned.
66 c2b8d366 Klaus Aehlig
data OwnerState = OwnShared | OwnExclusive deriving (Ord, Eq, Show)
67 c2b8d366 Klaus Aehlig
68 381889dc Klaus Aehlig
-- | Type describing indirect ownership on a lock. We keep the set
69 381889dc Klaus Aehlig
-- of all (lock, owner)-pairs for locks that are implied in the given
70 381889dc Klaus Aehlig
-- lock, annotated with the type of ownership (shared or exclusive).
71 381889dc Klaus Aehlig
type IndirectOwners a b = M.Map (a, b) OwnerState
72 381889dc Klaus Aehlig
73 381889dc Klaus Aehlig
-- | The state of a lock that is taken. Besides the state of the lock
74 381889dc Klaus Aehlig
-- itself, we also keep track of all other lock allocation that affect
75 381889dc Klaus Aehlig
-- the given lock by means of implication.
76 381889dc Klaus Aehlig
data AllocationState a b = Exclusive b (IndirectOwners a b)
77 381889dc Klaus Aehlig
                         | Shared (S.Set b) (IndirectOwners a b)
78 381889dc Klaus Aehlig
                         deriving (Eq, Show)
79 381889dc Klaus Aehlig
80 381889dc Klaus Aehlig
-- | Compute the set of indirect owners from the information about
81 381889dc Klaus Aehlig
-- indirect ownership.
82 381889dc Klaus Aehlig
indirectOwners :: (Ord a, Ord b) => M.Map (a, b) OwnerState -> S.Set b
83 381889dc Klaus Aehlig
indirectOwners = S.map snd . M.keysSet
84 381889dc Klaus Aehlig
85 381889dc Klaus Aehlig
-- | Compute the (zero or one-elment) set of exclusive indirect owners.
86 381889dc Klaus Aehlig
indirectExclusives :: (Ord a, Ord b) => M.Map (a, b) OwnerState -> S.Set b
87 381889dc Klaus Aehlig
indirectExclusives = indirectOwners . M.filter (== OwnExclusive)
88 381889dc Klaus Aehlig
89 c2b8d366 Klaus Aehlig
{-| Representation of a Lock allocation
90 c2b8d366 Klaus Aehlig
91 c2b8d366 Klaus Aehlig
To keep queries for locks efficient, we keep two
92 c2b8d366 Klaus Aehlig
associations, with the invariant that they fit
93 c2b8d366 Klaus Aehlig
together: the association from locks to their
94 c2b8d366 Klaus Aehlig
allocation state, and the association from an
95 c2b8d366 Klaus Aehlig
owner to the set of locks owned. As we do not
96 c2b8d366 Klaus Aehlig
export the constructor, the problem of keeping
97 c2b8d366 Klaus Aehlig
this invariant reduces to only exporting functions
98 c2b8d366 Klaus Aehlig
that keep the invariant.
99 c2b8d366 Klaus Aehlig
100 c2b8d366 Klaus Aehlig
-}
101 c2b8d366 Klaus Aehlig
102 c2b8d366 Klaus Aehlig
data LockAllocation a b =
103 381889dc Klaus Aehlig
  LockAllocation { laLocks :: M.Map a (AllocationState a b)
104 c2b8d366 Klaus Aehlig
                 , laOwned :: M.Map b (M.Map a OwnerState)
105 c2b8d366 Klaus Aehlig
                 }
106 c2b8d366 Klaus Aehlig
  deriving (Eq, Show)
107 c2b8d366 Klaus Aehlig
108 c2b8d366 Klaus Aehlig
-- | A state with all locks being free.
109 c2b8d366 Klaus Aehlig
emptyAllocation :: (Ord a, Ord b) => LockAllocation a b
110 c2b8d366 Klaus Aehlig
emptyAllocation =
111 c2b8d366 Klaus Aehlig
  LockAllocation { laLocks = M.empty
112 c2b8d366 Klaus Aehlig
                 , laOwned = M.empty
113 c2b8d366 Klaus Aehlig
                 }
114 c2b8d366 Klaus Aehlig
115 c6d48e16 Klaus Aehlig
-- | Obtain the locks held by a given owner. The locks are reported
116 c6d48e16 Klaus Aehlig
-- as a map from the owned locks to the form of ownership (OwnShared
117 c6d48e16 Klaus Aehlig
-- or OwnExclusive).
118 c2b8d366 Klaus Aehlig
listLocks :: Ord b => b -> LockAllocation a b -> M.Map a OwnerState
119 c2b8d366 Klaus Aehlig
listLocks owner = fromMaybe M.empty . M.lookup owner . laOwned
120 15208e95 Klaus Aehlig
121 15208e95 Klaus Aehlig
-- | Data Type describing a change request on a single lock.
122 15208e95 Klaus Aehlig
data LockRequest a = LockRequest { lockAffected :: a
123 15208e95 Klaus Aehlig
                                 , lockRequestType :: Maybe OwnerState
124 15208e95 Klaus Aehlig
                                 }
125 15208e95 Klaus Aehlig
                     deriving (Eq, Show)
126 15208e95 Klaus Aehlig
127 15208e95 Klaus Aehlig
-- | Lock request for an exclusive lock.
128 15208e95 Klaus Aehlig
requestExclusive :: a -> LockRequest a
129 15208e95 Klaus Aehlig
requestExclusive lock = LockRequest { lockAffected = lock
130 15208e95 Klaus Aehlig
                                    , lockRequestType = Just OwnExclusive }
131 15208e95 Klaus Aehlig
132 15208e95 Klaus Aehlig
-- | Lock request for a shared lock.
133 15208e95 Klaus Aehlig
requestShared :: a -> LockRequest a
134 15208e95 Klaus Aehlig
requestShared lock = LockRequest { lockAffected = lock
135 15208e95 Klaus Aehlig
                                 , lockRequestType = Just OwnShared }
136 15208e95 Klaus Aehlig
137 15208e95 Klaus Aehlig
-- | Request to release a lock.
138 15208e95 Klaus Aehlig
requestRelease :: a -> LockRequest a
139 15208e95 Klaus Aehlig
requestRelease lock = LockRequest { lockAffected = lock
140 15208e95 Klaus Aehlig
                                  , lockRequestType = Nothing }
141 15208e95 Klaus Aehlig
142 381889dc Klaus Aehlig
-- | Update the Allocation state of a lock according to a given
143 381889dc Klaus Aehlig
-- function.
144 381889dc Klaus Aehlig
updateAllocState :: (Ord a, Ord b)
145 381889dc Klaus Aehlig
                  => (Maybe (AllocationState a b) -> AllocationState a b)
146 381889dc Klaus Aehlig
                  -> LockAllocation a b -> a -> LockAllocation a b
147 381889dc Klaus Aehlig
updateAllocState f state lock =
148 381889dc Klaus Aehlig
  let locks' = M.alter (find (/= Shared S.empty M.empty) . Just . f)
149 381889dc Klaus Aehlig
                        lock (laLocks state)
150 381889dc Klaus Aehlig
  in state { laLocks = locks' }
151 381889dc Klaus Aehlig
152 15208e95 Klaus Aehlig
-- | Internal function to update the state according to a single
153 15208e95 Klaus Aehlig
-- lock request, assuming all prerequisites are met.
154 15208e95 Klaus Aehlig
updateLock :: (Ord a, Ord b)
155 15208e95 Klaus Aehlig
           => b
156 15208e95 Klaus Aehlig
           -> LockAllocation a b -> LockRequest a -> LockAllocation a b
157 15208e95 Klaus Aehlig
updateLock owner state (LockRequest lock (Just OwnExclusive)) =
158 381889dc Klaus Aehlig
  let locks = laLocks state
159 381889dc Klaus Aehlig
      lockstate' = case M.lookup lock locks of
160 381889dc Klaus Aehlig
        Just (Exclusive _ i) -> Exclusive owner i
161 381889dc Klaus Aehlig
        Just (Shared _ i) -> Exclusive owner i
162 381889dc Klaus Aehlig
        Nothing -> Exclusive owner M.empty
163 381889dc Klaus Aehlig
      locks' = M.insert lock lockstate' locks
164 15208e95 Klaus Aehlig
      ownersLocks' = M.insert lock OwnExclusive $ listLocks owner state
165 15208e95 Klaus Aehlig
      owned' = M.insert owner ownersLocks' $ laOwned state
166 15208e95 Klaus Aehlig
  in state { laLocks = locks', laOwned = owned' }
167 15208e95 Klaus Aehlig
updateLock owner state (LockRequest lock (Just OwnShared)) =
168 15208e95 Klaus Aehlig
  let ownersLocks' = M.insert lock OwnShared $ listLocks owner state
169 15208e95 Klaus Aehlig
      owned' = M.insert owner ownersLocks' $ laOwned state
170 15208e95 Klaus Aehlig
      locks = laLocks state
171 15208e95 Klaus Aehlig
      lockState' = case M.lookup lock locks of
172 381889dc Klaus Aehlig
        Just (Exclusive _ i) -> Shared (S.singleton owner) i
173 381889dc Klaus Aehlig
        Just (Shared s i) -> Shared (S.insert owner s) i
174 381889dc Klaus Aehlig
        _ -> Shared (S.singleton owner) M.empty
175 15208e95 Klaus Aehlig
      locks' = M.insert lock lockState' locks
176 15208e95 Klaus Aehlig
  in state { laLocks = locks', laOwned = owned' }
177 15208e95 Klaus Aehlig
updateLock owner state (LockRequest lock Nothing) =
178 15208e95 Klaus Aehlig
  let ownersLocks' = M.delete lock $ listLocks owner state
179 15208e95 Klaus Aehlig
      owned = laOwned state
180 15208e95 Klaus Aehlig
      owned' = if M.null ownersLocks'
181 15208e95 Klaus Aehlig
                 then M.delete owner owned
182 15208e95 Klaus Aehlig
                 else M.insert owner ownersLocks' owned
183 381889dc Klaus Aehlig
      update (Just (Exclusive x i)) = if x == owner
184 381889dc Klaus Aehlig
                                        then Shared S.empty i
185 381889dc Klaus Aehlig
                                        else Exclusive x i
186 381889dc Klaus Aehlig
      update (Just (Shared s i)) = Shared (S.delete owner s) i
187 381889dc Klaus Aehlig
      update Nothing = Shared S.empty M.empty
188 381889dc Klaus Aehlig
  in updateAllocState update (state { laOwned = owned' }) lock
189 381889dc Klaus Aehlig
190 381889dc Klaus Aehlig
-- | Update the set of indirect ownerships of a lock by the given function.
191 381889dc Klaus Aehlig
updateIndirectSet :: (Ord a, Ord b)
192 381889dc Klaus Aehlig
                  => (IndirectOwners a b -> IndirectOwners a b)
193 381889dc Klaus Aehlig
                  -> LockAllocation a b -> a -> LockAllocation a b
194 381889dc Klaus Aehlig
updateIndirectSet f =
195 381889dc Klaus Aehlig
  let update (Just (Exclusive x i)) = Exclusive x (f i)
196 381889dc Klaus Aehlig
      update (Just (Shared s i)) = Shared s (f i)
197 381889dc Klaus Aehlig
      update Nothing = Shared S.empty (f M.empty)
198 381889dc Klaus Aehlig
  in updateAllocState update
199 381889dc Klaus Aehlig
200 381889dc Klaus Aehlig
-- | Update all indirect onwerships of a given lock.
201 381889dc Klaus Aehlig
updateIndirects :: (Lock a, Ord b)
202 381889dc Klaus Aehlig
                => b
203 381889dc Klaus Aehlig
                -> LockAllocation a b -> LockRequest a -> LockAllocation a b
204 381889dc Klaus Aehlig
updateIndirects owner state req =
205 381889dc Klaus Aehlig
  let lock = lockAffected req
206 381889dc Klaus Aehlig
      fn = case lockRequestType req of
207 381889dc Klaus Aehlig
             Nothing -> M.delete (lock, owner)
208 381889dc Klaus Aehlig
             Just tp -> M.insert (lock, owner) tp
209 381889dc Klaus Aehlig
  in foldl (updateIndirectSet fn) state $ lockImplications lock
210 15208e95 Klaus Aehlig
211 15208e95 Klaus Aehlig
-- | Update the locks of an owner according to the given request. Return
212 15208e95 Klaus Aehlig
-- the pair of the new state and the result of the operation, which is the
213 15208e95 Klaus Aehlig
-- the set of owners on which the operation was blocked on. so an empty set is
214 15208e95 Klaus Aehlig
-- success, and the state is updated if, and only if, the returned set is emtpy.
215 15208e95 Klaus Aehlig
-- In that way, it can be used in atomicModifyIORef.
216 381889dc Klaus Aehlig
updateLocks :: (Lock a, Ord b)
217 381889dc Klaus Aehlig
            => b
218 381889dc Klaus Aehlig
            -> [LockRequest a]
219 381889dc Klaus Aehlig
            -> LockAllocation a b -> (LockAllocation a b, Result (S.Set b))
220 15208e95 Klaus Aehlig
updateLocks owner reqs state = genericResult ((,) state . Bad) (second Ok) $ do
221 1ca6b451 Klaus Aehlig
  unless ((==) (length reqs) . S.size . S.fromList $ map lockAffected reqs)
222 1ca6b451 Klaus Aehlig
    . runListHead (return ())
223 1ca6b451 Klaus Aehlig
                  (fail . (++) "Inconsitent requests for lock " . show) $ do
224 15208e95 Klaus Aehlig
      r <- reqs
225 15208e95 Klaus Aehlig
      r' <- reqs
226 15208e95 Klaus Aehlig
      guard $ r /= r'
227 15208e95 Klaus Aehlig
      guard $ lockAffected r == lockAffected r'
228 15208e95 Klaus Aehlig
      return $ lockAffected r
229 15208e95 Klaus Aehlig
  let current = listLocks owner state
230 15208e95 Klaus Aehlig
  unless (M.null current) $ do
231 15208e95 Klaus Aehlig
    let (highest, _) = M.findMax current
232 15208e95 Klaus Aehlig
        notHolding = not
233 15208e95 Klaus Aehlig
                     . any (uncurry (==) . ((M.lookup `flip` current) *** Just))
234 15208e95 Klaus Aehlig
        orderViolation l = fail $ "Order violation: requesting " ++ show l
235 15208e95 Klaus Aehlig
                                   ++ " while holding " ++ show highest
236 15208e95 Klaus Aehlig
    for_ reqs $ \req -> case req of
237 15208e95 Klaus Aehlig
      LockRequest lock (Just OwnExclusive)
238 15208e95 Klaus Aehlig
        | lock < highest && notHolding [ (lock, OwnExclusive) ]
239 15208e95 Klaus Aehlig
        -> orderViolation lock
240 15208e95 Klaus Aehlig
      LockRequest lock (Just OwnShared)
241 15208e95 Klaus Aehlig
        | lock < highest && notHolding [ (lock, OwnExclusive)
242 15208e95 Klaus Aehlig
                                       , (lock, OwnExclusive)]
243 15208e95 Klaus Aehlig
        -> orderViolation lock
244 15208e95 Klaus Aehlig
      _ -> Ok ()
245 956b83d6 Klaus Aehlig
  let sharedsHeld = M.keysSet $ M.filter (== OwnShared) current
246 956b83d6 Klaus Aehlig
      exclusivesRequested = map lockAffected
247 956b83d6 Klaus Aehlig
                            . filter ((== Just OwnExclusive) . lockRequestType)
248 956b83d6 Klaus Aehlig
                            $ reqs
249 956b83d6 Klaus Aehlig
  runListHead (return ()) fail $ do
250 956b83d6 Klaus Aehlig
    x <- exclusivesRequested
251 956b83d6 Klaus Aehlig
    i <- lockImplications x
252 956b83d6 Klaus Aehlig
    guard $ S.member i sharedsHeld
253 956b83d6 Klaus Aehlig
    return $ "Order violation: requesting exclusively " ++ show x
254 956b83d6 Klaus Aehlig
              ++ " while holding a shared lock on the group lock " ++ show i
255 956b83d6 Klaus Aehlig
              ++ " it belongs to."
256 15208e95 Klaus Aehlig
  let blockedOn (LockRequest  _ Nothing) = S.empty
257 15208e95 Klaus Aehlig
      blockedOn (LockRequest lock (Just OwnExclusive)) =
258 15208e95 Klaus Aehlig
        case M.lookup lock (laLocks state) of
259 381889dc Klaus Aehlig
          Just (Exclusive x i) ->
260 381889dc Klaus Aehlig
            S.singleton x `S.union` indirectOwners i
261 381889dc Klaus Aehlig
          Just (Shared xs i) ->
262 381889dc Klaus Aehlig
            xs `S.union` indirectOwners i
263 15208e95 Klaus Aehlig
          _ -> S.empty
264 15208e95 Klaus Aehlig
      blockedOn (LockRequest lock (Just OwnShared)) =
265 15208e95 Klaus Aehlig
        case M.lookup lock (laLocks state) of
266 381889dc Klaus Aehlig
          Just (Exclusive x i) ->
267 381889dc Klaus Aehlig
            S.singleton x `S.union` indirectExclusives i
268 381889dc Klaus Aehlig
          Just (Shared _ i) -> indirectExclusives i
269 381889dc Klaus Aehlig
          _ -> S.empty
270 381889dc Klaus Aehlig
  let indirectBlocked Nothing _ = S.empty
271 381889dc Klaus Aehlig
      indirectBlocked (Just OwnShared) lock =
272 381889dc Klaus Aehlig
        case M.lookup lock (laLocks state) of
273 381889dc Klaus Aehlig
          Just (Exclusive x _) -> S.singleton x
274 381889dc Klaus Aehlig
          _ -> S.empty
275 381889dc Klaus Aehlig
      indirectBlocked (Just OwnExclusive) lock =
276 381889dc Klaus Aehlig
        case M.lookup lock (laLocks state) of
277 381889dc Klaus Aehlig
          Just (Exclusive x _) -> S.singleton x
278 381889dc Klaus Aehlig
          Just (Shared xs _) -> xs
279 15208e95 Klaus Aehlig
          _ -> S.empty
280 381889dc Klaus Aehlig
  let direct = S.unions $ map blockedOn reqs
281 381889dc Klaus Aehlig
      indirect = reqs >>= \req ->
282 381889dc Klaus Aehlig
        map (indirectBlocked (lockRequestType req))
283 381889dc Klaus Aehlig
          . lockImplications $ lockAffected req
284 381889dc Klaus Aehlig
  let blocked = S.delete owner . S.unions $ direct:indirect
285 15208e95 Klaus Aehlig
  let state' = foldl (updateLock owner) state reqs
286 381889dc Klaus Aehlig
      state'' = foldl (updateIndirects owner) state' reqs
287 381889dc Klaus Aehlig
  return (if S.null blocked then state'' else state, blocked)
288 80004e70 Klaus Aehlig
289 80004e70 Klaus Aehlig
-- | Compute the state after an onwer releases all its locks.
290 80004e70 Klaus Aehlig
freeLocks :: (Lock a, Ord b) => LockAllocation a b -> b -> LockAllocation a b
291 80004e70 Klaus Aehlig
freeLocks state owner =
292 80004e70 Klaus Aehlig
  fst . flip (updateLocks owner) state . map requestRelease . M.keys
293 80004e70 Klaus Aehlig
    $ listLocks owner state
294 d8216f2d Klaus Aehlig
295 d8216f2d Klaus Aehlig
-- | Restrict the locks of a user to a given set.
296 d8216f2d Klaus Aehlig
intersectLocks :: (Lock a, Ord b) => b -> [a]
297 d8216f2d Klaus Aehlig
               -> LockAllocation a b -> LockAllocation a b
298 d8216f2d Klaus Aehlig
intersectLocks owner locks state =
299 d8216f2d Klaus Aehlig
  let lockset = S.fromList locks
300 d8216f2d Klaus Aehlig
      toFree = filter (not . flip S.member lockset)
301 d8216f2d Klaus Aehlig
                 . M.keys $ listLocks owner state
302 d8216f2d Klaus Aehlig
  in fst $ updateLocks owner (map requestRelease toFree) state
303 4b217f68 Klaus Aehlig
304 4b217f68 Klaus Aehlig
-- | Opportunistically allocate locks for a given user; return the set
305 4b217f68 Klaus Aehlig
-- of actually acquired. The signature is chosen to be suitable for
306 4b217f68 Klaus Aehlig
-- atomicModifyIORef.
307 4b217f68 Klaus Aehlig
opportunisticLockUnion :: (Lock a, Ord b)
308 4b217f68 Klaus Aehlig
                       => b -> [(a, OwnerState)]
309 4b217f68 Klaus Aehlig
                       -> LockAllocation a b -> (LockAllocation a b, S.Set a)
310 4b217f68 Klaus Aehlig
opportunisticLockUnion owner reqs state =
311 4b217f68 Klaus Aehlig
  let locks = listLocks owner state
312 4b217f68 Klaus Aehlig
      reqs' = sort $ filter (uncurry (<) . (flip M.lookup locks *** Just)) reqs
313 4b217f68 Klaus Aehlig
      maybeAllocate (s, success) (lock, ownstate) =
314 4b217f68 Klaus Aehlig
        let (s', result) = updateLocks owner
315 4b217f68 Klaus Aehlig
                                       [(if ownstate == OwnShared
316 4b217f68 Klaus Aehlig
                                           then requestShared
317 4b217f68 Klaus Aehlig
                                           else requestExclusive) lock]
318 4b217f68 Klaus Aehlig
                                       s
319 4b217f68 Klaus Aehlig
        in (s', if result == Ok S.empty then lock:success else success)
320 4b217f68 Klaus Aehlig
  in second S.fromList $ foldl maybeAllocate (state, []) reqs'
321 71dc39a1 Klaus Aehlig
322 71dc39a1 Klaus Aehlig
{-| Serializaiton of Lock Allocations
323 71dc39a1 Klaus Aehlig
324 71dc39a1 Klaus Aehlig
To serialize a lock allocation, we only remember which owner holds
325 71dc39a1 Klaus Aehlig
which locks at which level (shared or exclusive). From this information,
326 71dc39a1 Klaus Aehlig
everything else can be reconstructed, simply using updateLocks.
327 71dc39a1 Klaus Aehlig
-}
328 71dc39a1 Klaus Aehlig
329 71dc39a1 Klaus Aehlig
instance J.JSON OwnerState where
330 71dc39a1 Klaus Aehlig
  showJSON OwnShared = J.showJSON "shared"
331 71dc39a1 Klaus Aehlig
  showJSON OwnExclusive = J.showJSON "exclusive"
332 71dc39a1 Klaus Aehlig
  readJSON (J.JSString x) = let s = J.fromJSString x
333 71dc39a1 Klaus Aehlig
                            in case s of
334 71dc39a1 Klaus Aehlig
                              "shared" -> J.Ok OwnShared
335 71dc39a1 Klaus Aehlig
                              "exclusive" -> J.Ok OwnExclusive
336 71dc39a1 Klaus Aehlig
                              _ -> J.Error $ "Unknown owner type " ++ s
337 71dc39a1 Klaus Aehlig
  readJSON _ = J.Error "Owner type not encoded as a string"
338 71dc39a1 Klaus Aehlig
339 71dc39a1 Klaus Aehlig
-- | Read a lock-ownerstate pair from JSON.
340 71dc39a1 Klaus Aehlig
readLockOwnerstate :: (J.JSON a) => J.JSValue -> J.Result (a, OwnerState)
341 71dc39a1 Klaus Aehlig
readLockOwnerstate (J.JSArray [x, y]) = liftA2 (,) (J.readJSON x) (J.readJSON y)
342 71dc39a1 Klaus Aehlig
readLockOwnerstate x = fail $ "lock-ownerstate pairs are encoded as arrays"
343 71dc39a1 Klaus Aehlig
                              ++ " of length 2, but found " ++ show x
344 71dc39a1 Klaus Aehlig
345 71dc39a1 Klaus Aehlig
-- | Read an owner-lock pair from JSON.
346 71dc39a1 Klaus Aehlig
readOwnerLock :: (J.JSON a, J.JSON b)
347 71dc39a1 Klaus Aehlig
              => J.JSValue -> J.Result (b, [(a, OwnerState)])
348 71dc39a1 Klaus Aehlig
readOwnerLock (J.JSArray [x, J.JSArray ys]) =
349 71dc39a1 Klaus Aehlig
  liftA2 (,) (J.readJSON x) (mapM readLockOwnerstate ys)
350 71dc39a1 Klaus Aehlig
readOwnerLock x = fail $ "Expected pair of owner and list of owned locks,"
351 71dc39a1 Klaus Aehlig
                         ++ " but found " ++ show x
352 71dc39a1 Klaus Aehlig
353 71dc39a1 Klaus Aehlig
-- | Transform a lock-ownerstate pair into a LockRequest.
354 71dc39a1 Klaus Aehlig
toRequest :: (a, OwnerState) -> LockRequest a
355 71dc39a1 Klaus Aehlig
toRequest (a, OwnExclusive) = requestExclusive a
356 71dc39a1 Klaus Aehlig
toRequest (a, OwnShared) = requestShared a
357 71dc39a1 Klaus Aehlig
358 71dc39a1 Klaus Aehlig
-- | Obtain a LockAllocation from a given owner-locks list.
359 71dc39a1 Klaus Aehlig
-- The obtained allocation is the one obtained if the respective owners
360 71dc39a1 Klaus Aehlig
-- requested their locks sequentially.
361 71dc39a1 Klaus Aehlig
allocationFromOwners :: (Lock a, Ord b, Show b)
362 71dc39a1 Klaus Aehlig
                     => [(b, [(a, OwnerState)])]
363 71dc39a1 Klaus Aehlig
                     -> J.Result (LockAllocation a b)
364 71dc39a1 Klaus Aehlig
allocationFromOwners =
365 71dc39a1 Klaus Aehlig
  let allocateOneOwner s (o, req) = do
366 71dc39a1 Klaus Aehlig
        let (s', result) = updateLocks o (map toRequest req) s
367 71dc39a1 Klaus Aehlig
        when (result /= Ok S.empty) . fail
368 71dc39a1 Klaus Aehlig
          . (++) ("Inconsistent lock status for " ++ show o ++ ": ")
369 71dc39a1 Klaus Aehlig
          $ case result of
370 71dc39a1 Klaus Aehlig
            Bad err -> err
371 71dc39a1 Klaus Aehlig
            Ok blocked -> "blocked on " ++ show (S.toList blocked)
372 71dc39a1 Klaus Aehlig
        return s'
373 71dc39a1 Klaus Aehlig
  in foldM allocateOneOwner emptyAllocation
374 71dc39a1 Klaus Aehlig
375 71dc39a1 Klaus Aehlig
instance (Lock a, J.JSON a, Ord b, J.JSON b, Show b)
376 71dc39a1 Klaus Aehlig
           => J.JSON (LockAllocation a b) where
377 71dc39a1 Klaus Aehlig
  showJSON = J.showJSON . M.toList . M.map M.toList . laOwned
378 71dc39a1 Klaus Aehlig
  readJSON x = do
379 71dc39a1 Klaus Aehlig
    xs <- toArray x
380 71dc39a1 Klaus Aehlig
    owned <- mapM readOwnerLock xs
381 71dc39a1 Klaus Aehlig
    allocationFromOwners owned