summaryrefslogtreecommitdiffstats
path: root/libc/stdio
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:03:48 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:03:48 -0800
commit4e468ed2eb86a2406e14f1eca82072ee501d05fd (patch)
tree4e05b3c66eef86531e464521a3bf96a1864d4bf5 /libc/stdio
parenta27d2baa0c1a2ec70f47ea9199b1dd6762c8a349 (diff)
downloadbionic-4e468ed2eb86a2406e14f1eca82072ee501d05fd.zip
bionic-4e468ed2eb86a2406e14f1eca82072ee501d05fd.tar.gz
bionic-4e468ed2eb86a2406e14f1eca82072ee501d05fd.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'libc/stdio')
-rw-r--r--libc/stdio/fclose.c4
-rw-r--r--libc/stdio/flockfile.c212
2 files changed, 216 insertions, 0 deletions
diff --git a/libc/stdio/fclose.c b/libc/stdio/fclose.c
index bdf6e52..e94292b 100644
--- a/libc/stdio/fclose.c
+++ b/libc/stdio/fclose.c
@@ -36,6 +36,9 @@
#include <stdlib.h>
#include "local.h"
+/* BIONIC: remove any file lock associated with a FILE* pointer */
+extern void __fremovelock(FILE *fp);
+
int
fclose(FILE *fp)
{
@@ -57,5 +60,6 @@ fclose(FILE *fp)
FREELB(fp);
fp->_flags = 0; /* Release this FILE for reuse. */
fp->_r = fp->_w = 0; /* Mess up if reaccessed. */
+ __fremovelock(fp);
return (r);
}
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);
+ }
+}