RTS tidyup sweep, first phase
[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 "PosixSource.h"
10 #include "Rts.h"
11
12 #include "FileLock.h"
13 #include "Hash.h"
14 #include "RtsUtils.h"
15
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19
20 typedef struct {
21     dev_t device;
22     ino_t 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 the dev_t with the ino_t, hope this is good enough.
48     return hashWord(table, (StgWord)l->inode ^ (StgWord)l->device);
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, dev_t dev, ino_t 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 }