xref: /haiku/src/system/libroot/posix/pthread/pthread_rwlock.cpp (revision 42f51416eb68b0a79f50fe185b6d0ecb064c62dc)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <pthread.h>
7 
8 #include <new>
9 
10 #include <Debug.h>
11 
12 #include <AutoLocker.h>
13 #include <syscalls.h>
14 #include <user_mutex_defs.h>
15 #include <user_thread.h>
16 #include <util/DoublyLinkedList.h>
17 
18 #include "pthread_private.h"
19 
20 #define MAX_READER_COUNT	1000000
21 
22 #define RWLOCK_FLAG_SHARED	0x01
23 
24 
25 struct Waiter : DoublyLinkedListLinkImpl<Waiter> {
26 	Waiter(bool writer)
27 		:
28 		userThread(get_user_thread()),
29 		thread(find_thread(NULL)),
30 		writer(writer),
31 		queued(false)
32 	{
33 	}
34 
35 	user_thread*	userThread;
36 	thread_id		thread;
37 	status_t		status;
38 	bool			writer;
39 	bool			queued;
40 };
41 
42 typedef DoublyLinkedList<Waiter> WaiterList;
43 
44 
45 struct SharedRWLock {
46 	uint32_t	flags;
47 	int32_t		owner;
48 	int32_t		sem;
49 
50 	status_t Init()
51 	{
52 		flags = RWLOCK_FLAG_SHARED;
53 		owner = -1;
54 		sem = create_sem(MAX_READER_COUNT, "pthread rwlock");
55 
56 		return sem >= 0 ? B_OK : EAGAIN;
57 	}
58 
59 	status_t Destroy()
60 	{
61 		if (sem < 0)
62 			return B_BAD_VALUE;
63 		return delete_sem(sem) == B_OK ? B_OK : B_BAD_VALUE;
64 	}
65 
66 	status_t ReadLock(bigtime_t timeout)
67 	{
68 		return acquire_sem_etc(sem, 1,
69 			timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
70 	}
71 
72 	status_t WriteLock(bigtime_t timeout)
73 	{
74 		status_t error = acquire_sem_etc(sem, MAX_READER_COUNT,
75 			timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
76 		if (error == B_OK)
77 			owner = find_thread(NULL);
78 		return error;
79 	}
80 
81 	status_t Unlock()
82 	{
83 		if (find_thread(NULL) == owner) {
84 			owner = -1;
85 			return release_sem_etc(sem, MAX_READER_COUNT, 0);
86 		} else
87 			return release_sem(sem);
88 	}
89 };
90 
91 
92 struct LocalRWLock {
93 	uint32_t	flags;
94 	int32_t		owner;
95 	int32_t		mutex;
96 	int32_t		unused;
97 	int32_t		reader_count;
98 	int32_t		writer_count;
99 		// Note, that reader_count and writer_count are not used the same way.
100 		// writer_count includes the write lock owner as well as waiting
101 		// writers. reader_count includes read lock owners only.
102 	WaiterList	waiters;
103 
104 	status_t Init()
105 	{
106 		flags = 0;
107 		owner = -1;
108 		mutex = 0;
109 		reader_count = 0;
110 		writer_count = 0;
111 		new(&waiters) WaiterList;
112 
113 		return B_OK;
114 	}
115 
116 	status_t Destroy()
117 	{
118 		Locker locker(this);
119 		if (reader_count > 0 || waiters.Head() != NULL || writer_count > 0)
120 			return EBUSY;
121 		return B_OK;
122 	}
123 
124 	bool StructureLock()
125 	{
126 		// Enter critical region: lock the mutex
127 		int32 status = atomic_or((int32*)&mutex, B_USER_MUTEX_LOCKED);
128 
129 		// If already locked, call the kernel
130 		if ((status & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) != 0) {
131 			do {
132 				status = _kern_mutex_lock((int32*)&mutex, NULL, 0, 0);
133 			} while (status == B_INTERRUPTED);
134 
135 			if (status != B_OK)
136 				return false;
137 		}
138 		return true;
139 	}
140 
141 	void StructureUnlock()
142 	{
143 		// Exit critical region: unlock the mutex
144 		int32 status = atomic_and((int32*)&mutex,
145 			~(int32)B_USER_MUTEX_LOCKED);
146 
147 		if ((status & B_USER_MUTEX_WAITING) != 0)
148 			_kern_mutex_unlock((int32*)&mutex, 0);
149 	}
150 
151 	status_t ReadLock(bigtime_t timeout)
152 	{
153 		Locker locker(this);
154 
155 		if (writer_count == 0) {
156 			reader_count++;
157 			return B_OK;
158 		}
159 
160 		return _Wait(false, timeout);
161 	}
162 
163 	status_t WriteLock(bigtime_t timeout)
164 	{
165 		Locker locker(this);
166 
167 		if (reader_count == 0 && writer_count == 0) {
168 			writer_count++;
169 			owner = find_thread(NULL);
170 			return B_OK;
171 		}
172 
173 		return _Wait(true, timeout);
174 	}
175 
176 	status_t Unlock()
177 	{
178 		Locker locker(this);
179 
180 		if (find_thread(NULL) == owner) {
181 			writer_count--;
182 			owner = -1;
183 		} else
184 			reader_count--;
185 
186 		_Unblock();
187 
188 		return B_OK;
189 	}
190 
191 private:
192 	status_t _Wait(bool writer, bigtime_t timeout)
193 	{
194 		if (timeout == 0)
195 			return B_TIMED_OUT;
196 
197 		Waiter waiter(writer);
198 		waiters.Add(&waiter);
199 		waiter.queued = true;
200 		waiter.userThread->wait_status = 1;
201 
202 		if (writer)
203 			writer_count++;
204 
205 		StructureUnlock();
206 		status_t error = _kern_block_thread(
207 			timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
208 		StructureLock();
209 
210 		if (!waiter.queued)
211 			return waiter.status;
212 
213 		// we're still queued, which means an error (timeout, interrupt)
214 		// occurred
215 		waiters.Remove(&waiter);
216 
217 		if (writer)
218 			writer_count--;
219 
220 		_Unblock();
221 
222 		return error;
223 	}
224 
225 	void _Unblock()
226 	{
227 		// Check whether there any waiting threads at all and whether anyone
228 		// has the write lock
229 		Waiter* waiter = waiters.Head();
230 		if (waiter == NULL || owner >= 0)
231 			return;
232 
233 		// writer at head of queue?
234 		if (waiter->writer) {
235 			if (reader_count == 0) {
236 				waiter->status = B_OK;
237 				waiter->queued = false;
238 				waiters.Remove(waiter);
239 				owner = waiter->thread;
240 
241 				if (waiter->userThread->wait_status > 0)
242 					_kern_unblock_thread(waiter->thread, B_OK);
243 			}
244 			return;
245 		}
246 
247 		// wake up one or more readers -- we unblock more than one reader at
248 		// a time to save trips to the kernel
249 		while (!waiters.IsEmpty() && !waiters.Head()->writer) {
250 			static const int kMaxReaderUnblockCount = 128;
251 			thread_id readers[kMaxReaderUnblockCount];
252 			int readerCount = 0;
253 
254 			while (readerCount < kMaxReaderUnblockCount
255 					&& (waiter = waiters.Head()) != NULL
256 					&& !waiter->writer) {
257 				waiter->status = B_OK;
258 				waiter->queued = false;
259 				waiters.Remove(waiter);
260 
261 				if (waiter->userThread->wait_status > 0) {
262 					readers[readerCount++] = waiter->thread;
263 					reader_count++;
264 				}
265 			}
266 
267 			if (readerCount > 0)
268 				_kern_unblock_threads(readers, readerCount, B_OK);
269 		}
270 	}
271 
272 
273 	struct Locking {
274 		inline bool Lock(LocalRWLock* lockable)
275 		{
276 			return lockable->StructureLock();
277 		}
278 
279 		inline void Unlock(LocalRWLock* lockable)
280 		{
281 			lockable->StructureUnlock();
282 		}
283 	};
284 	typedef AutoLocker<LocalRWLock, Locking> Locker;
285 };
286 
287 
288 static void inline
289 assert_dummy()
290 {
291 	STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(SharedRWLock));
292 	STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(LocalRWLock));
293 }
294 
295 
296 // #pragma mark - public lock functions
297 
298 
299 int
300 pthread_rwlock_init(pthread_rwlock_t* lock, const pthread_rwlockattr_t* _attr)
301 {
302 	pthread_rwlockattr* attr = _attr != NULL ? *_attr : NULL;
303 	bool shared = attr != NULL && (attr->flags & RWLOCK_FLAG_SHARED) != 0;
304 
305 	if (shared)
306 		return ((SharedRWLock*)lock)->Init();
307 	else
308 		return ((LocalRWLock*)lock)->Init();
309 }
310 
311 
312 int
313 pthread_rwlock_destroy(pthread_rwlock_t* lock)
314 {
315 	if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
316 		return ((SharedRWLock*)lock)->Destroy();
317 	else
318 		return ((LocalRWLock*)lock)->Destroy();
319 }
320 
321 
322 int
323 pthread_rwlock_rdlock(pthread_rwlock_t* lock)
324 {
325 	if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
326 		return ((SharedRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT);
327 	else
328 		return ((LocalRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT);
329 }
330 
331 
332 int
333 pthread_rwlock_tryrdlock(pthread_rwlock_t* lock)
334 {
335 	status_t error;
336 	if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
337 		error = ((SharedRWLock*)lock)->ReadLock(0);
338 	else
339 		error = ((LocalRWLock*)lock)->ReadLock(0);
340 
341 	return error == B_TIMED_OUT ? EBUSY : error;
342 }
343 
344 
345 int pthread_rwlock_timedrdlock(pthread_rwlock_t* lock,
346 	const struct timespec *timeout)
347 {
348 	bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL
349 		+ timeout->tv_nsec / 1000LL;
350 
351 	status_t error;
352 	if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
353 		error = ((SharedRWLock*)lock)->ReadLock(timeoutMicros);
354 	else
355 		error = ((LocalRWLock*)lock)->ReadLock(timeoutMicros);
356 
357 	return error == B_TIMED_OUT ? EBUSY : error;
358 }
359 
360 
361 int
362 pthread_rwlock_wrlock(pthread_rwlock_t* lock)
363 {
364 	if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
365 		return ((SharedRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT);
366 	else
367 		return ((LocalRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT);
368 }
369 
370 
371 int
372 pthread_rwlock_trywrlock(pthread_rwlock_t* lock)
373 {
374 	status_t error;
375 	if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
376 		error = ((SharedRWLock*)lock)->WriteLock(0);
377 	else
378 		error = ((LocalRWLock*)lock)->WriteLock(0);
379 
380 	return error == B_TIMED_OUT ? EBUSY : error;
381 }
382 
383 
384 int
385 pthread_rwlock_timedwrlock(pthread_rwlock_t* lock,
386 	const struct timespec *timeout)
387 {
388 	bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL
389 		+ timeout->tv_nsec / 1000LL;
390 
391 	status_t error;
392 	if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
393 		error = ((SharedRWLock*)lock)->WriteLock(timeoutMicros);
394 	else
395 		error = ((LocalRWLock*)lock)->WriteLock(timeoutMicros);
396 
397 	return error == B_TIMED_OUT ? EBUSY : error;
398 }
399 
400 
401 int
402 pthread_rwlock_unlock(pthread_rwlock_t* lock)
403 {
404 	if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
405 		return ((SharedRWLock*)lock)->Unlock();
406 	else
407 		return ((LocalRWLock*)lock)->Unlock();
408 }
409 
410 
411 // #pragma mark - public attribute functions
412 
413 
414 int
415 pthread_rwlockattr_init(pthread_rwlockattr_t* _attr)
416 {
417 	pthread_rwlockattr* attr = (pthread_rwlockattr*)malloc(
418 		sizeof(pthread_rwlockattr));
419 	if (attr == NULL)
420 		return B_NO_MEMORY;
421 
422 	attr->flags = 0;
423 	*_attr = attr;
424 
425 	return 0;
426 }
427 
428 
429 int
430 pthread_rwlockattr_destroy(pthread_rwlockattr_t* _attr)
431 {
432 	pthread_rwlockattr* attr = *_attr;
433 
434 	free(attr);
435 	return 0;
436 }
437 
438 
439 int
440 pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* _attr, int* shared)
441 {
442 	pthread_rwlockattr* attr = *_attr;
443 
444 	*shared = (attr->flags & RWLOCK_FLAG_SHARED) != 0
445 		? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE;
446 	return 0;
447 }
448 
449 
450 int
451 pthread_rwlockattr_setpshared(pthread_rwlockattr_t* _attr, int shared)
452 {
453 	pthread_rwlockattr* attr = *_attr;
454 
455 	if (shared == PTHREAD_PROCESS_SHARED)
456 		attr->flags |= RWLOCK_FLAG_SHARED;
457 	else
458 		attr->flags &= ~RWLOCK_FLAG_SHARED;
459 
460 	return 0;
461 }
462 
463