Move file locking into the RTS, fixing #629, #1109
[ghc-hetmet.git] / rts / posix / FileLock.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 2007
4  *
5  * File locking support as required by Haskell 98
6  *
7  * ---------------------------------------------------------------------------*/
8  
9 #include "Rts.h"
10 #include "Hash.h"
11 #include "FileLock.h"
12 #include "RtsUtils.h"
13
14 #include <unistd.h>
15 #include <sys/stat.h>
16 #include <errno.h>
17
18 typedef struct {
19     dev_t device;
20     ino_t inode;
21     int   readers; // >0 : readers,  <0 : writers
22 } Lock;
23
24 // Two hash tables.  The first maps objects (device/inode pairs) to
25 // Lock objects containing the number of active readers or writers.  The
26 // second maps file descriptors to lock objects, so that we can unlock
27 // by FD without needing to fstat() again.
28 static HashTable *obj_hash;
29 static HashTable *fd_hash;
30
31 static int cmpLocks(StgWord w1, StgWord w2)
32 {
33     Lock *l1 = (Lock *)w1;
34     Lock *l2 = (Lock *)w2;
35     return (l1->device == l2->device && l1->inode == l2->inode);
36 }
37
38 static int hashLock(HashTable *table, StgWord w)
39 {
40     Lock *l = (Lock *)w;
41     // Just xor the dev_t with the ino_t, hope this is good enough.
42     return hashWord(table, (StgWord)l->inode ^ (StgWord)l->device);
43 }
44
45 void
46 initFileLocking(void)
47 {
48     obj_hash = allocHashTable_(hashLock, cmpLocks);
49     fd_hash  = allocHashTable(); /* ordinary word-based table */
50 }
51
52 static void
53 freeLock(void *lock)
54 {
55     stgFree(lock);
56 }
57
58 void
59 freeFileLocking(void)
60 {
61     freeHashTable(obj_hash, freeLock);
62     freeHashTable(fd_hash,  NULL);
63 }
64
65 int
66 lockFile(int fd, dev_t dev, ino_t ino, int for_writing)
67 {
68     Lock key, *lock;
69
70     key.device = dev;
71     key.inode  = ino;
72
73     lock = lookupHashTable(obj_hash, (StgWord)&key);
74
75     if (lock == NULL)
76     {
77         lock = stgMallocBytes(sizeof(Lock), "lockFile");
78         lock->device = dev;
79         lock->inode  = ino;
80         lock->readers = for_writing ? -1 : 1;
81         insertHashTable(obj_hash, (StgWord)lock, (void *)lock);
82         insertHashTable(fd_hash, fd, lock);
83         return 0;
84     }
85     else
86     {
87         // single-writer/multi-reader locking:
88         if (for_writing || lock->readers < 0) {
89             return -1;
90         }
91         lock->readers++;
92         return 0;
93     }
94 }
95
96 int
97 unlockFile(int fd)
98 {
99     Lock *lock;
100
101     lock = lookupHashTable(fd_hash, fd);
102     if (lock == NULL) {
103         // errorBelch("unlockFile: fd %d not found", fd); 
104         // This is normal: we didn't know when calling unlockFile
105         // whether this FD referred to a locked file or not.
106         return 1;
107     }
108
109     if (lock->readers < 0) {
110         lock->readers++;
111     } else {
112         lock->readers--;
113     }
114
115     if (lock->readers == 0) {
116         removeHashTable(obj_hash, (StgWord)lock, NULL);
117         stgFree(lock);
118     }
119     removeHashTable(fd_hash, fd, NULL);
120     return 0;
121 }