GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/thread/rthread_file.c Lines: 0 52 0.0 %
Date: 2017-11-13 Branches: 0 46 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: rthread_file.c,v 1.2 2017/08/15 06:38:41 guenther Exp $	*/
2
/*
3
 * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 * 3. All advertising materials mentioning features or use of this software
15
 *    must display the following acknowledgement:
16
 *	This product includes software developed by John Birrell.
17
 * 4. Neither the name of the author nor the names of any co-contributors
18
 *    may be used to endorse or promote products derived from this software
19
 *    without specific prior written permission.
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
 * SUCH DAMAGE.
32
 *
33
 * $FreeBSD: uthread_file.c,v 1.9 1999/08/28 00:03:32 peter Exp $
34
 *
35
 * POSIX stdio FILE locking functions. These assume that the locking
36
 * is only required at FILE structure level, not at file descriptor
37
 * level too.
38
 *
39
 */
40
41
#include <sys/queue.h>
42
#include <pthread.h>
43
#include <stdint.h>
44
#include <stdio.h>
45
#include <stdlib.h>
46
47
#include "rthread.h"
48
#include "rthread_cb.h"
49
50
/*
51
 * The FILE lock structure. The FILE *fp is locked if the owner is
52
 * not NULL. If not locked, the file lock structure can be
53
 * reassigned to a different file by setting fp.
54
 */
55
struct	file_lock {
56
	LIST_ENTRY(file_lock)	entry;	/* Entry if file list.       */
57
	FILE			*fp;	/* The target file.          */
58
	struct pthread_queue	lockers;
59
	pthread_t		owner;
60
	int			count;
61
};
62
63
/*
64
 * The number of file lock lists into which the file pointer is
65
 * hashed. Ideally, the FILE structure size would have been increased,
66
 * but this causes incompatibility, so separate data structures are
67
 * required.
68
 */
69
#define NUM_HEADS	128
70
71
/*
72
 * This macro casts a file pointer to a long integer and right
73
 * shifts this by the number of bytes in a pointer. The shifted
74
 * value is then remaindered using the maximum number of hash
75
 * entries to produce and index into the array of static lock
76
 * structures. If there is a collision, a linear search of the
77
 * dynamic list of locks linked to each static lock is perfomed.
78
 */
79
#define file_idx(_p)	((int)((((uintptr_t) _p) >> sizeof(void *)) % NUM_HEADS))
80
81
/*
82
 * Global array of file locks. The first lock for each hash bucket is
83
 * allocated statically in the hope that there won't be too many
84
 * collisions that require a malloc and an element added to the list.
85
 */
86
static struct static_file_lock {
87
	LIST_HEAD(file_list_head, file_lock) head;
88
	struct	file_lock	fl;
89
} flh[NUM_HEADS];
90
91
/* Lock for accesses to the hash table: */
92
static _atomic_lock_t	hash_lock	= _SPINLOCK_UNLOCKED;
93
94
/*
95
 * Find a lock structure for a FILE, return NULL if the file is
96
 * not locked:
97
 */
98
static
99
struct file_lock *
100
find_lock(int idx, FILE *fp)
101
{
102
	struct file_lock *p;
103
104
	/* Check if the file is locked using the static structure: */
105
	if (flh[idx].fl.fp == fp && flh[idx].fl.owner != NULL)
106
		/* Return a pointer to the static lock: */
107
		p = &flh[idx].fl;
108
	else {
109
		/* Point to the first dynamic lock: */
110
		p = LIST_FIRST(&flh[idx].head);
111
112
		/*
113
		 * Loop through the dynamic locks looking for the
114
		 * target file:
115
		 */
116
		while (p != NULL && (p->fp != fp || p->owner == NULL))
117
			/* Not this file, try the next: */
118
			p = LIST_NEXT(p, entry);
119
	}
120
	return(p);
121
}
122
123
/*
124
 * Lock a file, assuming that there is no lock structure currently
125
 * assigned to it.
126
 */
127
static
128
struct file_lock *
129
do_lock(int idx, FILE *fp)
130
{
131
	struct file_lock *p;
132
133
	/* Check if the static structure is not being used: */
134
	if (flh[idx].fl.owner == NULL) {
135
		/* Return a pointer to the static lock: */
136
		p = &flh[idx].fl;
137
	}
138
	else {
139
		/* Point to the first dynamic lock: */
140
		p = LIST_FIRST(&flh[idx].head);
141
142
		/*
143
		 * Loop through the dynamic locks looking for a
144
		 * lock structure that is not being used:
145
		 */
146
		while (p != NULL && p->owner != NULL)
147
			/* This one is used, try the next: */
148
			p = LIST_NEXT(p, entry);
149
	}
150
151
	/*
152
	 * If an existing lock structure has not been found,
153
	 * allocate memory for a new one:
154
	 */
155
	if (p == NULL && (p = (struct file_lock *)
156
	    malloc(sizeof(struct file_lock))) != NULL) {
157
		/* Add the new element to the list: */
158
		LIST_INSERT_HEAD(&flh[idx].head, p, entry);
159
	}
160
161
	/* Check if there is a lock structure to acquire: */
162
	if (p != NULL) {
163
		/* Acquire the lock for the running thread: */
164
		p->fp		= fp;
165
		p->owner	= pthread_self();
166
		p->count	= 1;
167
		TAILQ_INIT(&p->lockers);
168
	}
169
	return(p);
170
}
171
172
void
173
_thread_flockfile(FILE * fp)
174
{
175
	int	idx = file_idx(fp);
176
	struct	file_lock	*p;
177
	pthread_t	self = pthread_self();
178
179
	/* Lock the hash table: */
180
	_spinlock(&hash_lock);
181
182
	/* Get a pointer to any existing lock for the file: */
183
	if ((p = find_lock(idx, fp)) == NULL) {
184
		/*
185
		 * The file is not locked, so this thread can
186
		 * grab the lock:
187
		 */
188
		do_lock(idx, fp);
189
190
	/*
191
	 * The file is already locked, so check if the
192
	 * running thread is the owner:
193
	 */
194
	} else if (p->owner == self) {
195
		/*
196
		 * The running thread is already the
197
		 * owner, so increment the count of
198
		 * the number of times it has locked
199
		 * the file:
200
		 */
201
		p->count++;
202
	} else {
203
		/*
204
		 * The file is locked for another thread.
205
		 * Append this thread to the queue of
206
		 * threads waiting on the lock.
207
		 */
208
		TAILQ_INSERT_TAIL(&p->lockers,self,waiting);
209
		while (p->owner != self) {
210
			__thrsleep(self, 0, NULL, &hash_lock, NULL);
211
			_spinlock(&hash_lock);
212
		}
213
	}
214
215
	/* Unlock the hash table: */
216
	_spinunlock(&hash_lock);
217
}
218
219
int
220
_thread_ftrylockfile(FILE * fp)
221
{
222
	int	ret = -1;
223
	int	idx = file_idx(fp);
224
	struct	file_lock	*p;
225
226
	/* Lock the hash table: */
227
	_spinlock(&hash_lock);
228
229
	/* Get a pointer to any existing lock for the file: */
230
	if ((p = find_lock(idx, fp)) == NULL) {
231
		/*
232
		 * The file is not locked, so this thread can
233
		 * grab the lock:
234
		 */
235
		p = do_lock(idx, fp);
236
237
	/*
238
	 * The file is already locked, so check if the
239
	 * running thread is the owner:
240
	 */
241
	} else if (p->owner == pthread_self()) {
242
		/*
243
		 * The running thread is already the
244
		 * owner, so increment the count of
245
		 * the number of times it has locked
246
		 * the file:
247
		 */
248
		p->count++;
249
	} else {
250
		/*
251
		 * The file is locked for another thread,
252
		 * so this try fails.
253
		 */
254
		p = NULL;
255
	}
256
257
	/* Unlock the hash table: */
258
	_spinunlock(&hash_lock);
259
260
	/* Check if the lock was obtained: */
261
	if (p != NULL)
262
		/* Return success: */
263
		ret = 0;
264
265
	return (ret);
266
}
267
268
void
269
_thread_funlockfile(FILE * fp)
270
{
271
	int	idx = file_idx(fp);
272
	struct	file_lock	*p;
273
274
	/* Lock the hash table: */
275
	_spinlock(&hash_lock);
276
277
	/*
278
	 * Get a pointer to the lock for the file and check that
279
	 * the running thread is the one with the lock:
280
	 */
281
	if ((p = find_lock(idx, fp)) != NULL && p->owner == pthread_self()) {
282
		/*
283
		 * Check if this thread has locked the FILE
284
		 * more than once:
285
		 */
286
		if (--p->count == 0) {
287
			/* Get the new owner of the lock: */
288
			if ((p->owner = TAILQ_FIRST(&p->lockers)) != NULL) {
289
				/* Pop the thread off the queue: */
290
				TAILQ_REMOVE(&p->lockers,p->owner,waiting);
291
292
				/*
293
				 * This is the first lock for the new
294
				 * owner:
295
				 */
296
				p->count = 1;
297
298
				__thrwakeup(p->owner, 1);
299
			}
300
		}
301
	}
302
303
	/* Unlock the hash table: */
304
	_spinunlock(&hash_lock);
305
}