Comments about the let/app invariant
[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 StgWord key = l->inode ^ (l->inode >> 32) ^ l->device ^ (l->device >> 32);
48 // Just xor all 32-bit words of inode and device, hope this is good enough.
49 return hashWord(table, key);
50 }
51
52 void
53 initFileLocking(void)
54 {
55 obj_hash = allocHashTable_(hashLock, cmpLocks);
56 fd_hash = allocHashTable(); /* ordinary word-based table */
57 #ifdef THREADED_RTS
58 initMutex(&file_lock_mutex);
59 #endif
60 }
61
62 static void
63 freeLock(void *lock)
64 {
65 stgFree(lock);
66 }
67
68 void
69 freeFileLocking(void)
70 {
71 freeHashTable(obj_hash, freeLock);
72 freeHashTable(fd_hash, NULL);
73 #ifdef THREADED_RTS
74 closeMutex(&file_lock_mutex);
75 #endif
76 }
77
78 int
79 lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing)
80 {
81 Lock key, *lock;
82
83 ACQUIRE_LOCK(&file_lock_mutex);
84
85 key.device = dev;
86 key.inode = ino;
87
88 lock = lookupHashTable(obj_hash, (StgWord)&key);
89
90 if (lock == NULL)
91 {
92 lock = stgMallocBytes(sizeof(Lock), "lockFile");
93 lock->device = dev;
94 lock->inode = ino;
95 lock->readers = for_writing ? -1 : 1;
96 insertHashTable(obj_hash, (StgWord)lock, (void *)lock);
97 insertHashTable(fd_hash, fd, lock);
98 RELEASE_LOCK(&file_lock_mutex);
99 return 0;
100 }
101 else
102 {
103 // single-writer/multi-reader locking:
104 if (for_writing || lock->readers < 0) {
105 RELEASE_LOCK(&file_lock_mutex);
106 return -1;
107 }
108 insertHashTable(fd_hash, fd, lock);
109 lock->readers++;
110 RELEASE_LOCK(&file_lock_mutex);
111 return 0;
112 }
113 }
114
115 int
116 unlockFile(int fd)
117 {
118 Lock *lock;
119
120 ACQUIRE_LOCK(&file_lock_mutex);
121
122 lock = lookupHashTable(fd_hash, fd);
123 if (lock == NULL) {
124 // errorBelch("unlockFile: fd %d not found", fd);
125 // This is normal: we didn't know when calling unlockFile
126 // whether this FD referred to a locked file or not.
127 RELEASE_LOCK(&file_lock_mutex);
128 return 1;
129 }
130
131 if (lock->readers < 0) {
132 lock->readers++;
133 } else {
134 lock->readers--;
135 }
136
137 if (lock->readers == 0) {
138 removeHashTable(obj_hash, (StgWord)lock, NULL);
139 stgFree(lock);
140 }
141 removeHashTable(fd_hash, fd, NULL);
142
143 RELEASE_LOCK(&file_lock_mutex);
144 return 0;
145 }