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