FileLock.c 3.31 KB
Newer Older
1 2 3 4
/* -----------------------------------------------------------------------------
 *
 * (c) The GHC Team, 2007
 *
Ian Lynagh's avatar
Ian Lynagh committed
5
 * File locking support as required by Haskell
6 7
 *
 * ---------------------------------------------------------------------------*/
8

Simon Marlow's avatar
Simon Marlow committed
9
#include "PosixSource.h"
10
#include "Rts.h"
Simon Marlow's avatar
Simon Marlow committed
11

12
#include "FileLock.h"
Simon Marlow's avatar
Simon Marlow committed
13
#include "Hash.h"
14 15
#include "RtsUtils.h"

16 17
#include <sys/types.h>
#include <unistd.h>
18 19 20
#include <errno.h>

typedef struct {
pcapriotti's avatar
pcapriotti committed
21 22
    StgWord64 device;
    StgWord64 inode;
23 24 25 26 27 28 29 30 31 32
    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;

33 34 35 36
#ifdef THREADED_RTS
static Mutex file_lock_mutex;
#endif

37 38 39 40 41 42 43
static int cmpLocks(StgWord w1, StgWord w2)
{
    Lock *l1 = (Lock *)w1;
    Lock *l2 = (Lock *)w2;
    return (l1->device == l2->device && l1->inode == l2->inode);
}

44
static int hashLock(const HashTable *table, StgWord w)
45 46
{
    Lock *l = (Lock *)w;
Austin Seipp's avatar
Austin Seipp committed
47
    StgWord key = l->inode ^ (l->inode >> 32) ^ l->device ^ (l->device >> 32);
pcapriotti's avatar
pcapriotti committed
48
    // Just xor all 32-bit words of inode and device, hope this is good enough.
Austin Seipp's avatar
Austin Seipp committed
49
    return hashWord(table, key);
50 51 52 53 54 55 56
}

void
initFileLocking(void)
{
    obj_hash = allocHashTable_(hashLock, cmpLocks);
    fd_hash  = allocHashTable(); /* ordinary word-based table */
57 58 59
#ifdef THREADED_RTS
    initMutex(&file_lock_mutex);
#endif
60 61 62 63 64 65 66 67 68 69 70 71 72
}

static void
freeLock(void *lock)
{
    stgFree(lock);
}

void
freeFileLocking(void)
{
    freeHashTable(obj_hash, freeLock);
    freeHashTable(fd_hash,  NULL);
73 74 75
#ifdef THREADED_RTS
    closeMutex(&file_lock_mutex);
#endif
76 77 78
}

int
pcapriotti's avatar
pcapriotti committed
79
lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing)
80 81 82
{
    Lock key, *lock;

83 84
    ACQUIRE_LOCK(&file_lock_mutex);

85 86 87 88 89 90 91 92 93 94 95 96 97
    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);
98
        RELEASE_LOCK(&file_lock_mutex);
99 100 101 102 103 104
        return 0;
    }
    else
    {
        // single-writer/multi-reader locking:
        if (for_writing || lock->readers < 0) {
105
            RELEASE_LOCK(&file_lock_mutex);
106 107
            return -1;
        }
Simon Marlow's avatar
Simon Marlow committed
108
        insertHashTable(fd_hash, fd, lock);
109
        lock->readers++;
110
        RELEASE_LOCK(&file_lock_mutex);
111 112 113 114 115 116 117 118 119
        return 0;
    }
}

int
unlockFile(int fd)
{
    Lock *lock;

120 121
    ACQUIRE_LOCK(&file_lock_mutex);

122 123
    lock = lookupHashTable(fd_hash, fd);
    if (lock == NULL) {
124
        // errorBelch("unlockFile: fd %d not found", fd);
125 126
        // This is normal: we didn't know when calling unlockFile
        // whether this FD referred to a locked file or not.
127
        RELEASE_LOCK(&file_lock_mutex);
128 129 130 131 132 133 134 135 136 137 138 139 140 141
        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);
142 143

    RELEASE_LOCK(&file_lock_mutex);
144 145
    return 0;
}