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