blob: bfb081cdef382b965400a647ebeb120e563ab33e [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29/* implement flockfile(), ftrylockfile() and funlockfile()
30 *
31 * we can't use the OpenBSD implementation which uses kernel-specific
32 * APIs not available on Linux.
33 *
34 * Ideally, this would be trivially implemented by adding a
35 * pthread_mutex_t field to struct __sFILE as defined in
36 * <stdio.h>.
37 *
38 * However, since we don't want to bring pthread into the mix
39 * as well as change the size of a public API/ABI structure,
40 * we're going to store the data out-of-band.
41 *
42 * we use a hash-table to map FILE* pointers to recursive mutexes
43 * fclose() will call __fremovelock() defined below to remove
44 * a pointer from the table.
45 *
46 * the behaviour, if fclose() is called while the corresponding
47 * file is locked is totally undefined.
48 */
49#include <stdio.h>
50#include <pthread.h>
51#include <string.h>
52
53/* a node in the hash table */
54typedef struct FileLock {
55 struct FileLock* next;
56 FILE* file;
57 pthread_mutex_t mutex;
58} FileLock;
59
60/* use a static hash table. We assume that we're not going to
61 * lock a really large number of FILE* objects on an embedded
62 * system.
63 */
64#define FILE_LOCK_BUCKETS 32
65
66typedef struct {
67 pthread_mutex_t lock;
68 FileLock* buckets[ FILE_LOCK_BUCKETS ];
69} LockTable;
70
71static LockTable* _lockTable;
72static pthread_once_t _lockTable_once = PTHREAD_ONCE_INIT;
73
74static void
75lock_table_init( void )
76{
77 _lockTable = malloc(sizeof(*_lockTable));
78 if (_lockTable != NULL) {
79 pthread_mutex_init(&_lockTable->lock, NULL);
80 memset(_lockTable->buckets, 0, sizeof(_lockTable->buckets));
81 }
82}
83
84static LockTable*
85lock_table_lock( void )
86{
87 pthread_once( &_lockTable_once, lock_table_init );
88 pthread_mutex_lock( &_lockTable->lock );
89 return _lockTable;
90}
91
92static void
93lock_table_unlock( LockTable* t )
94{
95 pthread_mutex_unlock( &t->lock );
96}
97
98static FileLock**
99lock_table_lookup( LockTable* t, FILE* f )
100{
101 uint32_t hash = (uint32_t)(void*)f;
102 FileLock** pnode;
103
104 hash = (hash >> 2) ^ (hash << 17);
105 pnode = &t->buckets[hash % FILE_LOCK_BUCKETS];
106 for (;;) {
107 FileLock* node = *pnode;
108 if (node == NULL || node->file == f)
109 break;
110 pnode = &node->next;
111 }
112 return pnode;
113}
114
115void
116flockfile(FILE * fp)
117{
118 LockTable* t = lock_table_lock();
119
120 if (t != NULL) {
121 FileLock** lookup = lock_table_lookup(t, fp);
122 FileLock* lock = *lookup;
123
124 if (lock == NULL) {
125 pthread_mutexattr_t attr;
126
127 /* create a new node in the hash table */
128 lock = malloc(sizeof(*lock));
129 if (lock == NULL) {
130 lock_table_unlock(t);
131 return;
132 }
133 lock->next = NULL;
134 lock->file = fp;
135
136 pthread_mutexattr_init(&attr);
137 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
138 pthread_mutex_init( &lock->mutex, &attr );
139
140 *lookup = lock;
141 }
142 lock_table_unlock(t);
143
144 /* we assume that another thread didn't destroy 'lock'
145 * by calling fclose() on the FILE*. This can happen if
146 * the client is *really* buggy, but we don't care about
147 * such code here.
148 */
149 pthread_mutex_lock(&lock->mutex);
150 }
151}
152
153
154int
155ftrylockfile(FILE *fp)
156{
157 int ret = -1;
158 LockTable* t = lock_table_lock();
159
160 if (t != NULL) {
161 FileLock** lookup = lock_table_lookup(t, fp);
162 FileLock* lock = *lookup;
163
164 lock_table_unlock(t);
165
166 /* see above comment about why we assume that 'lock' can
167 * be accessed from here
168 */
169 if (lock != NULL && !pthread_mutex_trylock(&lock->mutex)) {
170 ret = 0; /* signal success */
171 }
172 }
173 return ret;
174}
175
176void
177funlockfile(FILE * fp)
178{
179 LockTable* t = lock_table_lock();
180
181 if (t != NULL) {
182 FileLock** lookup = lock_table_lookup(t, fp);
183 FileLock* lock = *lookup;
184
185 if (lock != NULL)
186 pthread_mutex_unlock(&lock->mutex);
187
188 lock_table_unlock(t);
189 }
190}
191
192
193/* called from fclose() to remove the file lock */
194void
195__fremovelock(FILE* fp)
196{
197 LockTable* t = lock_table_lock();
198
199 if (t != NULL) {
200 FileLock** lookup = lock_table_lookup(t, fp);
201 FileLock* lock = *lookup;
202
203 if (lock != NULL) {
204 *lookup = lock->next;
205 lock->file = NULL;
206 }
207 lock_table_unlock(t);
André Goddard Rosa051ea9b2010-01-30 22:45:07 -0200208 free(lock);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800209 }
210}