• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 */
54 typedef 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 
66 typedef struct {
67     pthread_mutex_t   lock;
68     FileLock*         buckets[ FILE_LOCK_BUCKETS ];
69 } LockTable;
70 
71 static LockTable*      _lockTable;
72 static pthread_once_t  _lockTable_once = PTHREAD_ONCE_INIT;
73 
74 static void
lock_table_init(void)75 lock_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 
84 static LockTable*
lock_table_lock(void)85 lock_table_lock( void )
86 {
87     pthread_once( &_lockTable_once, lock_table_init );
88     pthread_mutex_lock( &_lockTable->lock );
89     return _lockTable;
90 }
91 
92 static void
lock_table_unlock(LockTable * t)93 lock_table_unlock( LockTable*  t )
94 {
95     pthread_mutex_unlock( &t->lock );
96 }
97 
98 static FileLock**
lock_table_lookup(LockTable * t,FILE * f)99 lock_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 
115 void
flockfile(FILE * fp)116 flockfile(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 
154 int
ftrylockfile(FILE * fp)155 ftrylockfile(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 
176 void
funlockfile(FILE * fp)177 funlockfile(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 */
194 __LIBC_HIDDEN__ void
__fremovelock(FILE * fp)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);
208         free(lock);
209     }
210 }
211