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