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