diff options
-rw-r--r-- | package.nix | 13 | ||||
-rw-r--r-- | src/Trivmix.hs | 32 | ||||
-rw-r--r-- | trivmix.cabal | 3 | ||||
-rw-r--r-- | trivmix.nix | 17 |
4 files changed, 41 insertions, 24 deletions
diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..e15704a --- /dev/null +++ b/package.nix | |||
@@ -0,0 +1,13 @@ | |||
1 | { fetchgit | ||
2 | , stdenv | ||
3 | , callPackage | ||
4 | }: | ||
5 | |||
6 | stdenv.lib.overrideDerivation (callPackage ./trivmix.nix {}) (attrs : { | ||
7 | src = fetchgit { | ||
8 | url = git://git.yggdrasil.li/trivmix; | ||
9 | # nix-shell -p nix-prefetch-scripts --command 'nix-prefetch-git git://git.yggdrasil.li/beuteltier' 2>&1 | grep -E '(git revision|hash) is ' | sed -r 's/git revision is /rev = "/' | sed -r 's/hash is /sha256 = "/' | sed -r 's/$/";/' | ||
10 | rev = "f7ea4713957f57fa395779d6db0b020d6eb36512"; | ||
11 | sha256 = "e6dc0910cee98ee3161617ebcca763f3f3e120497182d43629447a487213f8a1"; | ||
12 | }; | ||
13 | }) | ||
diff --git a/src/Trivmix.hs b/src/Trivmix.hs index 0c1a1a4..79b3804 100644 --- a/src/Trivmix.hs +++ b/src/Trivmix.hs | |||
@@ -27,6 +27,7 @@ import Control.Exception | |||
27 | import System.IO.Error | 27 | import System.IO.Error |
28 | import System.IO | 28 | import System.IO |
29 | 29 | ||
30 | import System.FileLock | ||
30 | import System.INotify | 31 | import System.INotify |
31 | 32 | ||
32 | import Data.Char | 33 | import Data.Char |
@@ -164,12 +165,13 @@ handleFiles :: INotify -> MVar Level -> [FilePath] -> IO () | |||
164 | handleFiles inotify level files = do | 165 | handleFiles inotify level files = do |
165 | initLevel <- readMVar level | 166 | initLevel <- readMVar level |
166 | levelChanges <- (newChan :: IO (Chan Level)) | 167 | levelChanges <- (newChan :: IO (Chan Level)) |
168 | stderrLock <- newMVar | ||
167 | let | 169 | let |
168 | handleFile file = do | 170 | handleFile file = do |
169 | levelChanges' <- dupChan levelChanges | 171 | levelChanges' <- dupChan levelChanges |
170 | forkIO $ forever $ do -- Broadcast level changes and update all files | 172 | forkIO $ forever $ do -- Broadcast level changes and update all files |
171 | readChan levelChanges' >>= writeLevel file | 173 | readChan levelChanges' >>= writeLevel file stderrLock |
172 | addWatch inotify watchedAttrs file (const $ readLevel levelChanges level file) | 174 | addWatch inotify watchedAttrs file (const $ readLevel levelChanges level file stderrLock) |
173 | mapM handleFile files | 175 | mapM handleFile files |
174 | forkIO $ forever $ do | 176 | forkIO $ forever $ do |
175 | readChan levelChanges >>= swapMVar level | 177 | readChan levelChanges >>= swapMVar level |
@@ -193,7 +195,7 @@ onStateFile file initial action = do | |||
193 | hPutStrLn stderr $ "Removing ‘" ++ file ++ "’ (file)" | 195 | hPutStrLn stderr $ "Removing ‘" ++ file ++ "’ (file)" |
194 | removeFile file | 196 | removeFile file |
195 | acquireDir = (flip mapM) (reverse createDirs) $ \directory -> do | 197 | acquireDir = (flip mapM) (reverse createDirs) $ \directory -> do |
196 | hPutStrLn stderr $ "Creating ‘" ++ directory ++ "’ (dir" | 198 | hPutStrLn stderr $ "Creating ‘" ++ directory ++ "’ (dir)" |
197 | createDirectory directory | 199 | createDirectory directory |
198 | setFileMode directory defDirectoryMode | 200 | setFileMode directory defDirectoryMode |
199 | releaseDir = (flip mapM) createDirs $ \directory -> do | 201 | releaseDir = (flip mapM) createDirs $ \directory -> do |
@@ -214,19 +216,21 @@ takeWhileM pred (x:xs) = do | |||
214 | False -> do | 216 | False -> do |
215 | return [] | 217 | return [] |
216 | 218 | ||
217 | readLevel :: Chan Level -> MVar Level -> FilePath -> IO () | 219 | readLevel :: Chan Level -> MVar Level -> FilePath -> MVar () -> IO () |
218 | readLevel levelChan current file = catch action handler | 220 | readLevel levelChan current file stderrLock = catch action handler |
219 | where | 221 | where |
220 | action = do | 222 | action = do |
221 | level <- readFile file >>= readIO . stripSpace | 223 | level <- withFileLock file Shared $ readFile file >>= readIO . stripSpace |
222 | oldLevel <- readMVar current | 224 | oldLevel <- readMVar current |
223 | when (oldLevel /= level) $ do | 225 | when (oldLevel /= level) $ do |
224 | writeChan levelChan level | 226 | writeChan levelChan level |
225 | hPutStrLn stderr $ "Detected new level: " ++ (show level) | 227 | withMVarLock stderrLock $ |
228 | hPutStrLn stderr $ "Detected new level: " ++ (show level) | ||
226 | handler e = if isUserError e | 229 | handler e = if isUserError e |
227 | then do | 230 | then do |
228 | hPutStrLn stderr $ "Could not parse new level from ‘" ++ file ++ "’ - overwriting." | 231 | withMVarLock stderrLock $ |
229 | readMVar current >>= writeLevel file | 232 | hPutStrLn stderr $ "Could not parse new level from ‘" ++ file ++ "’ - overwriting." |
233 | readMVar current >>= writeLevel file stderrLock | ||
230 | else throw e | 234 | else throw e |
231 | stripSpace = reverse . stripSpace' . reverse . stripSpace' | 235 | stripSpace = reverse . stripSpace' . reverse . stripSpace' |
232 | stripSpace' [] = [] | 236 | stripSpace' [] = [] |
@@ -234,7 +238,11 @@ readLevel levelChan current file = catch action handler | |||
234 | then stripSpace' xs | 238 | then stripSpace' xs |
235 | else l | 239 | else l |
236 | 240 | ||
237 | writeLevel :: FilePath -> Level -> IO () | 241 | writeLevel :: FilePath -> MVar () -> Level -> IO () |
238 | writeLevel file level = do | 242 | writeLevel file stderrLock level = withFileLock file Exclusive $ do |
239 | hPutStrLn stderr $ "Writing out level ‘" ++ (show level) ++ "’ to ‘" ++ file ++ "’" | 243 | withMVarLock stderrLock $ |
244 | hPutStrLn stderr $ "Writing out level ‘" ++ (show level) ++ "’ to ‘" ++ file ++ "’" | ||
240 | writeFile file (show level ++ "\n") | 245 | writeFile file (show level ++ "\n") |
246 | |||
247 | withMVarLock :: MVar () -> IO a -> IO a | ||
248 | withMVarLock lock = bracket_ (putMVar lock ()) (takeMVar lock) | ||
diff --git a/trivmix.cabal b/trivmix.cabal index b0d77a7..74001d7 100644 --- a/trivmix.cabal +++ b/trivmix.cabal | |||
@@ -2,7 +2,7 @@ | |||
2 | -- documentation, see http://haskell.org/cabal/users-guide/ | 2 | -- documentation, see http://haskell.org/cabal/users-guide/ |
3 | 3 | ||
4 | name: trivmix | 4 | name: trivmix |
5 | version: 2.3.0 | 5 | version: 2.4.0 |
6 | -- synopsis: | 6 | -- synopsis: |
7 | -- description: | 7 | -- description: |
8 | license: PublicDomain | 8 | license: PublicDomain |
@@ -30,6 +30,7 @@ executable trivmix | |||
30 | , explicit-exception >=0.1 && <1 | 30 | , explicit-exception >=0.1 && <1 |
31 | , process >=1.2 && <2 | 31 | , process >=1.2 && <2 |
32 | , case-insensitive >=1.2 && <2 | 32 | , case-insensitive >=1.2 && <2 |
33 | , filelock >=0.1 && <1 | ||
33 | hs-source-dirs: src | 34 | hs-source-dirs: src |
34 | default-language: Haskell2010 | 35 | default-language: Haskell2010 |
35 | ghc-options: -threaded | 36 | ghc-options: -threaded |
diff --git a/trivmix.nix b/trivmix.nix index a5ccb9b..f4228d2 100644 --- a/trivmix.nix +++ b/trivmix.nix | |||
@@ -1,23 +1,18 @@ | |||
1 | # This file was auto-generated by cabal2nix. Please do NOT edit manually! | 1 | # This file was auto-generated by cabal2nix. Please do NOT edit manually! |
2 | 2 | ||
3 | { cabal, filepath, hinotify, jack, optparseApplicative | 3 | { cabal, caseInsensitive, explicitException, filelock, filepath |
4 | , transformers, explicitException, process, caseInsensitive | 4 | , hinotify, jack, optparseApplicative, transformers |
5 | , fetchgit | ||
6 | }: | 5 | }: |
7 | 6 | ||
8 | cabal.mkDerivation (self: { | 7 | cabal.mkDerivation (self: { |
9 | pname = "trivmix"; | 8 | pname = "trivmix"; |
10 | version = "2.3.0"; | 9 | version = "2.4.0"; |
11 | src = fetchgit { | 10 | src = ./.; |
12 | url = git://git.yggdrasil.li/trivmix; | ||
13 | rev = "d5189cba07f63c3d2f8c575a31c1734f7c9aeed6"; | ||
14 | sha256 = "88b215dc2d24b875359835b5a75acdd1d6172ad4d4e3fad9ce4cdc3ffec51ba3"; | ||
15 | }; | ||
16 | isLibrary = false; | 11 | isLibrary = false; |
17 | isExecutable = true; | 12 | isExecutable = true; |
18 | buildDepends = [ | 13 | buildDepends = [ |
19 | filepath hinotify jack optparseApplicative transformers explicitException process | 14 | caseInsensitive explicitException filelock filepath hinotify jack |
20 | caseInsensitive | 15 | optparseApplicative transformers |
21 | ]; | 16 | ]; |
22 | meta = { | 17 | meta = { |
23 | license = self.stdenv.lib.licenses.publicDomain; | 18 | license = self.stdenv.lib.licenses.publicDomain; |