diff options
Diffstat (limited to 'libc/stdio/flockfile.c')
-rw-r--r-- | libc/stdio/flockfile.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/libc/stdio/flockfile.c b/libc/stdio/flockfile.c new file mode 100644 index 0000000..a81879e --- /dev/null +++ b/libc/stdio/flockfile.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* implement flockfile(), ftrylockfile() and funlockfile() + * + * we can't use the OpenBSD implementation which uses kernel-specific + * APIs not available on Linux. + * + * Ideally, this would be trivially implemented by adding a + * pthread_mutex_t field to struct __sFILE as defined in + * <stdio.h>. + * + * However, since we don't want to bring pthread into the mix + * as well as change the size of a public API/ABI structure, + * we're going to store the data out-of-band. + * + * we use a hash-table to map FILE* pointers to recursive mutexes + * fclose() will call __fremovelock() defined below to remove + * a pointer from the table. + * + * the behaviour, if fclose() is called while the corresponding + * file is locked is totally undefined. + */ +#include <stdio.h> +#include <pthread.h> +#include <string.h> + +/* a node in the hash table */ +typedef struct FileLock { + struct FileLock* next; + FILE* file; + pthread_mutex_t mutex; +} FileLock; + +/* use a static hash table. We assume that we're not going to + * lock a really large number of FILE* objects on an embedded + * system. + */ +#define FILE_LOCK_BUCKETS 32 + +typedef struct { + pthread_mutex_t lock; + FileLock* buckets[ FILE_LOCK_BUCKETS ]; +} LockTable; + +static LockTable* _lockTable; +static pthread_once_t _lockTable_once = PTHREAD_ONCE_INIT; + +static void +lock_table_init( void ) +{ + _lockTable = malloc(sizeof(*_lockTable)); + if (_lockTable != NULL) { + pthread_mutex_init(&_lockTable->lock, NULL); + memset(_lockTable->buckets, 0, sizeof(_lockTable->buckets)); + } +} + +static LockTable* +lock_table_lock( void ) +{ + pthread_once( &_lockTable_once, lock_table_init ); + pthread_mutex_lock( &_lockTable->lock ); + return _lockTable; +} + +static void +lock_table_unlock( LockTable* t ) +{ + pthread_mutex_unlock( &t->lock ); +} + +static FileLock** +lock_table_lookup( LockTable* t, FILE* f ) +{ + uint32_t hash = (uint32_t)(void*)f; + FileLock** pnode; + + hash = (hash >> 2) ^ (hash << 17); + pnode = &t->buckets[hash % FILE_LOCK_BUCKETS]; + for (;;) { + FileLock* node = *pnode; + if (node == NULL || node->file == f) + break; + pnode = &node->next; + } + return pnode; +} + +void +flockfile(FILE * fp) +{ + LockTable* t = lock_table_lock(); + + if (t != NULL) { + FileLock** lookup = lock_table_lookup(t, fp); + FileLock* lock = *lookup; + + if (lock == NULL) { + pthread_mutexattr_t attr; + + /* create a new node in the hash table */ + lock = malloc(sizeof(*lock)); + if (lock == NULL) { + lock_table_unlock(t); + return; + } + lock->next = NULL; + lock->file = fp; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init( &lock->mutex, &attr ); + + *lookup = lock; + } + lock_table_unlock(t); + + /* we assume that another thread didn't destroy 'lock' + * by calling fclose() on the FILE*. This can happen if + * the client is *really* buggy, but we don't care about + * such code here. + */ + pthread_mutex_lock(&lock->mutex); + } +} + + +int +ftrylockfile(FILE *fp) +{ + int ret = -1; + LockTable* t = lock_table_lock(); + + if (t != NULL) { + FileLock** lookup = lock_table_lookup(t, fp); + FileLock* lock = *lookup; + + lock_table_unlock(t); + + /* see above comment about why we assume that 'lock' can + * be accessed from here + */ + if (lock != NULL && !pthread_mutex_trylock(&lock->mutex)) { + ret = 0; /* signal success */ + } + } + return ret; +} + +void +funlockfile(FILE * fp) +{ + LockTable* t = lock_table_lock(); + + if (t != NULL) { + FileLock** lookup = lock_table_lookup(t, fp); + FileLock* lock = *lookup; + + if (lock != NULL) + pthread_mutex_unlock(&lock->mutex); + + lock_table_unlock(t); + } +} + + +/* called from fclose() to remove the file lock */ +void +__fremovelock(FILE* fp) +{ + LockTable* t = lock_table_lock(); + + if (t != NULL) { + FileLock** lookup = lock_table_lookup(t, fp); + FileLock* lock = *lookup; + + if (lock != NULL) { + *lookup = lock->next; + lock->file = NULL; + } + lock_table_unlock(t); + + if (lock != NULL) + free(lock); + } +} |