Remove references to Haskell 98
[ghc-hetmet.git] / rts / posix / 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 <sys/stat.h>
18 #include <unistd.h>
19 #include <errno.h>
20
21 typedef struct {
22     dev_t device;
23     ino_t inode;
24     int   readers; // >0 : readers,  <0 : writers
25 } Lock;
26
27 // Two hash tables.  The first maps objects (device/inode pairs) to
28 // Lock objects containing the number of active readers or writers.  The
29 // second maps file descriptors to lock objects, so that we can unlock
30 // by FD without needing to fstat() again.
31 static HashTable *obj_hash;
32 static HashTable *fd_hash;
33
34 #ifdef THREADED_RTS
35 static Mutex file_lock_mutex;
36 #endif
37
38 static int cmpLocks(StgWord w1, StgWord w2)
39 {
40     Lock *l1 = (Lock *)w1;
41     Lock *l2 = (Lock *)w2;
42     return (l1->device == l2->device && l1->inode == l2->inode);
43 }
44
45 static int hashLock(HashTable *table, StgWord w)
46 {
47     Lock *l = (Lock *)w;
48     // Just xor the dev_t with the ino_t, hope this is good enough.
49     return hashWord(table, (StgWord)l->inode ^ (StgWord)l->device);
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, dev_t dev, ino_t 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 }