1258b34c5SIngo Weinhold/*
2258b34c5SIngo Weinhold * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
3258b34c5SIngo Weinhold * Distributed under the terms of the MIT License.
4258b34c5SIngo Weinhold */
5258b34c5SIngo Weinhold
6258b34c5SIngo Weinhold
7258b34c5SIngo Weinhold#include <locks.h>
8258b34c5SIngo Weinhold
9258b34c5SIngo Weinhold#include <OS.h>
10258b34c5SIngo Weinhold
11258b34c5SIngo Weinhold#include <syscalls.h>
12258b34c5SIngo Weinhold#include <user_thread.h>
13258b34c5SIngo Weinhold
14258b34c5SIngo Weinhold
15258b34c5SIngo Weinholdtypedef struct rw_lock_waiter {
16258b34c5SIngo Weinhold	rw_lock_waiter *	next;
17258b34c5SIngo Weinhold	thread_id			thread;
18258b34c5SIngo Weinhold	bool				writer;
19258b34c5SIngo Weinhold} rw_lock_waiter;
20258b34c5SIngo Weinhold
21258b34c5SIngo Weinhold
22258b34c5SIngo Weinholdstatic status_t
23258b34c5SIngo Weinholdrw_lock_wait(rw_lock *lock, bool writer)
24258b34c5SIngo Weinhold{
25258b34c5SIngo Weinhold	rw_lock_waiter waiter;
26258b34c5SIngo Weinhold	waiter.thread = find_thread(NULL);
27258b34c5SIngo Weinhold	waiter.next = NULL;
28258b34c5SIngo Weinhold	waiter.writer = writer;
29258b34c5SIngo Weinhold
30258b34c5SIngo Weinhold	if (lock->waiters != NULL)
31258b34c5SIngo Weinhold		lock->last_waiter->next = &waiter;
32258b34c5SIngo Weinhold	else
33258b34c5SIngo Weinhold		lock->waiters = &waiter;
34258b34c5SIngo Weinhold
35258b34c5SIngo Weinhold	lock->last_waiter = &waiter;
36258b34c5SIngo Weinhold
37258b34c5SIngo Weinhold	// the rw_lock is locked when entering, release it before blocking
38258b34c5SIngo Weinhold	get_user_thread()->wait_status = 1;
39258b34c5SIngo Weinhold	mutex_unlock(&lock->lock);
40258b34c5SIngo Weinhold
41258b34c5SIngo Weinhold	status_t result;
42258b34c5SIngo Weinhold	do {
43258b34c5SIngo Weinhold		result = _kern_block_thread(0, 0);
44258b34c5SIngo Weinhold	} while (result == B_INTERRUPTED);
45258b34c5SIngo Weinhold
46258b34c5SIngo Weinhold	// and lock it again before returning
47258b34c5SIngo Weinhold	mutex_lock(&lock->lock);
48258b34c5SIngo Weinhold	return result;
49258b34c5SIngo Weinhold}
50258b34c5SIngo Weinhold
51258b34c5SIngo Weinhold
52258b34c5SIngo Weinholdstatic void
53258b34c5SIngo Weinholdrw_lock_unblock(rw_lock *lock)
54258b34c5SIngo Weinhold{
55258b34c5SIngo Weinhold	// this is called locked
56258b34c5SIngo Weinhold	if (lock->holder >= 0)
57258b34c5SIngo Weinhold		return;
58258b34c5SIngo Weinhold
59258b34c5SIngo Weinhold	rw_lock_waiter *waiter = lock->waiters;
60258b34c5SIngo Weinhold	if (waiter == NULL)
61258b34c5SIngo Weinhold		return;
62258b34c5SIngo Weinhold
63258b34c5SIngo Weinhold	if (waiter->writer) {
64258b34c5SIngo Weinhold		if (lock->reader_count > 0)
65258b34c5SIngo Weinhold			return;
66258b34c5SIngo Weinhold
67258b34c5SIngo Weinhold		lock->waiters = waiter->next;
68258b34c5SIngo Weinhold		lock->holder = waiter->thread;
69258b34c5SIngo Weinhold		_kern_unblock_thread(waiter->thread, B_OK);
70258b34c5SIngo Weinhold		return;
71258b34c5SIngo Weinhold	}
72258b34c5SIngo Weinhold
73258b34c5SIngo Weinhold	while (waiter != NULL && !waiter->writer) {
74258b34c5SIngo Weinhold		lock->reader_count++;
75258b34c5SIngo Weinhold		lock->waiters = waiter->next;
76258b34c5SIngo Weinhold		_kern_unblock_thread(waiter->thread, B_OK);
77258b34c5SIngo Weinhold		waiter = lock->waiters;
78258b34c5SIngo Weinhold	}
79258b34c5SIngo Weinhold}
80258b34c5SIngo Weinhold
81258b34c5SIngo Weinhold
82f7127458SIngo Weinholdvoid
83b916156aSJulian Harnath__rw_lock_init(rw_lock *lock, const char *name)
84258b34c5SIngo Weinhold{
85f7127458SIngo Weinhold	rw_lock_init_etc(lock, name, 0);
86f7127458SIngo Weinhold}
87f7127458SIngo Weinhold
88f7127458SIngo Weinhold
89f7127458SIngo Weinholdvoid
90b916156aSJulian Harnath__rw_lock_init_etc(rw_lock *lock, const char *name, uint32 flags)
91f7127458SIngo Weinhold{
92258b34c5SIngo Weinhold	lock->waiters = NULL;
93258b34c5SIngo Weinhold	lock->holder = -1;
94258b34c5SIngo Weinhold	lock->reader_count = 0;
95258b34c5SIngo Weinhold	lock->writer_count = 0;
96258b34c5SIngo Weinhold	lock->owner_count = 0;
97f7127458SIngo Weinhold	mutex_init_etc(&lock->lock, name, flags);
98258b34c5SIngo Weinhold}
99258b34c5SIngo Weinhold
100258b34c5SIngo Weinhold
101258b34c5SIngo Weinholdvoid
102b916156aSJulian Harnath__rw_lock_destroy(rw_lock *lock)
103258b34c5SIngo Weinhold{
104258b34c5SIngo Weinhold	mutex_lock(&lock->lock);
105258b34c5SIngo Weinhold
106258b34c5SIngo Weinhold	rw_lock_waiter *waiter = lock->waiters;
107258b34c5SIngo Weinhold	while (waiter != NULL) {
108258b34c5SIngo Weinhold		_kern_unblock_thread(waiter->thread, B_ERROR);
109258b34c5SIngo Weinhold		waiter = waiter->next;
110258b34c5SIngo Weinhold	}
111258b34c5SIngo Weinhold
112258b34c5SIngo Weinhold	mutex_destroy(&lock->lock);
113258b34c5SIngo Weinhold}
114258b34c5SIngo Weinhold
115258b34c5SIngo Weinhold
116258b34c5SIngo Weinholdstatus_t
117b916156aSJulian Harnath__rw_lock_read_lock(rw_lock *lock)
118258b34c5SIngo Weinhold{
119258b34c5SIngo Weinhold	MutexLocker locker(lock->lock);
120258b34c5SIngo Weinhold
121258b34c5SIngo Weinhold	if (lock->writer_count == 0) {
122258b34c5SIngo Weinhold		lock->reader_count++;
123258b34c5SIngo Weinhold		return B_OK;
124258b34c5SIngo Weinhold	}
125258b34c5SIngo Weinhold
126258b34c5SIngo Weinhold	if (lock->holder == find_thread(NULL)) {
127258b34c5SIngo Weinhold		lock->owner_count++;
128258b34c5SIngo Weinhold		return B_OK;
129258b34c5SIngo Weinhold	}
130258b34c5SIngo Weinhold
131258b34c5SIngo Weinhold	return rw_lock_wait(lock, false);
132258b34c5SIngo Weinhold}
133258b34c5SIngo Weinhold
134258b34c5SIngo Weinhold
135258b34c5SIngo Weinholdstatus_t
136b916156aSJulian Harnath__rw_lock_read_unlock(rw_lock *lock)
137258b34c5SIngo Weinhold{
138258b34c5SIngo Weinhold	MutexLocker locker(lock->lock);
139258b34c5SIngo Weinhold
140258b34c5SIngo Weinhold	if (lock->holder == find_thread(NULL)) {
141258b34c5SIngo Weinhold		if (--lock->owner_count > 0)
142258b34c5SIngo Weinhold			return B_OK;
143258b34c5SIngo Weinhold
144258b34c5SIngo Weinhold		// this originally has been a write lock
145258b34c5SIngo Weinhold		lock->writer_count--;
146258b34c5SIngo Weinhold		lock->holder = -1;
147258b34c5SIngo Weinhold
148258b34c5SIngo Weinhold		rw_lock_unblock(lock);
149258b34c5SIngo Weinhold		return B_OK;
150258b34c5SIngo Weinhold	}
151258b34c5SIngo Weinhold
152258b34c5SIngo Weinhold	if (lock->reader_count <= 0) {
153258b34c5SIngo Weinhold		debugger("rw_lock not read locked");
154258b34c5SIngo Weinhold		return B_ERROR;
155258b34c5SIngo Weinhold	}
156258b34c5SIngo Weinhold
157258b34c5SIngo Weinhold	lock->reader_count--;
158258b34c5SIngo Weinhold	rw_lock_unblock(lock);
159258b34c5SIngo Weinhold	return B_OK;
160258b34c5SIngo Weinhold}
161258b34c5SIngo Weinhold
162258b34c5SIngo Weinhold
163258b34c5SIngo Weinholdstatus_t
164b916156aSJulian Harnath__rw_lock_write_lock(rw_lock *lock)
165258b34c5SIngo Weinhold{
166258b34c5SIngo Weinhold	MutexLocker locker(lock->lock);
167258b34c5SIngo Weinhold
168258b34c5SIngo Weinhold	if (lock->reader_count == 0 && lock->writer_count == 0) {
169258b34c5SIngo Weinhold		lock->writer_count++;
170258b34c5SIngo Weinhold		lock->holder = find_thread(NULL);
171258b34c5SIngo Weinhold		lock->owner_count = 1;
172258b34c5SIngo Weinhold		return B_OK;
173258b34c5SIngo Weinhold	}
174258b34c5SIngo Weinhold
175258b34c5SIngo Weinhold	if (lock->holder == find_thread(NULL)) {
176258b34c5SIngo Weinhold		lock->owner_count++;
177258b34c5SIngo Weinhold		return B_OK;
178258b34c5SIngo Weinhold	}
179258b34c5SIngo Weinhold
180258b34c5SIngo Weinhold	lock->writer_count++;
181258b34c5SIngo Weinhold
182258b34c5SIngo Weinhold	status_t result = rw_lock_wait(lock, true);
183258b34c5SIngo Weinhold	if (result != B_OK)
184258b34c5SIngo Weinhold		return result;
185258b34c5SIngo Weinhold
186258b34c5SIngo Weinhold	if (lock->holder != find_thread(NULL)) {
187258b34c5SIngo Weinhold		debugger("write locked but holder not set");
188258b34c5SIngo Weinhold		return B_ERROR;
189258b34c5SIngo Weinhold	}
190258b34c5SIngo Weinhold
191258b34c5SIngo Weinhold	lock->owner_count = 1;
192258b34c5SIngo Weinhold	return B_OK;
193258b34c5SIngo Weinhold}
194258b34c5SIngo Weinhold
195258b34c5SIngo Weinhold
196258b34c5SIngo Weinholdstatus_t
197b916156aSJulian Harnath__rw_lock_write_unlock(rw_lock *lock)
198258b34c5SIngo Weinhold{
199258b34c5SIngo Weinhold	MutexLocker locker(lock->lock);
200258b34c5SIngo Weinhold
201258b34c5SIngo Weinhold	if (lock->holder != find_thread(NULL)) {
202258b34c5SIngo Weinhold		debugger("rw_lock not write locked");
203258b34c5SIngo Weinhold		return B_ERROR;
204258b34c5SIngo Weinhold	}
205258b34c5SIngo Weinhold
206258b34c5SIngo Weinhold	if (--lock->owner_count > 0)
207258b34c5SIngo Weinhold		return B_OK;
208258b34c5SIngo Weinhold
209258b34c5SIngo Weinhold	lock->writer_count--;
210258b34c5SIngo Weinhold	lock->holder = -1;
211258b34c5SIngo Weinhold	rw_lock_unblock(lock);
212258b34c5SIngo Weinhold	return B_OK;
213258b34c5SIngo Weinhold}
214