xref: /haiku/src/system/libroot/os/locks/rw_lock.cpp (revision 002f37b0cca92e4cf72857c72ac95db5a8b09615)
1 /*
2  * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <locks.h>
8 
9 #include <OS.h>
10 
11 #include <syscalls.h>
12 #include <user_thread.h>
13 
14 
15 typedef struct rw_lock_waiter {
16 	rw_lock_waiter *	next;
17 	thread_id			thread;
18 	bool				writer;
19 } rw_lock_waiter;
20 
21 
22 static status_t
23 rw_lock_wait(rw_lock *lock, bool writer)
24 {
25 	rw_lock_waiter waiter;
26 	waiter.thread = find_thread(NULL);
27 	waiter.next = NULL;
28 	waiter.writer = writer;
29 
30 	if (lock->waiters != NULL)
31 		lock->last_waiter->next = &waiter;
32 	else
33 		lock->waiters = &waiter;
34 
35 	lock->last_waiter = &waiter;
36 
37 	// the rw_lock is locked when entering, release it before blocking
38 	get_user_thread()->wait_status = 1;
39 	mutex_unlock(&lock->lock);
40 
41 	status_t result;
42 	do {
43 		result = _kern_block_thread(0, 0);
44 	} while (result == B_INTERRUPTED);
45 
46 	// and lock it again before returning
47 	mutex_lock(&lock->lock);
48 	return result;
49 }
50 
51 
52 static void
53 rw_lock_unblock(rw_lock *lock)
54 {
55 	// this is called locked
56 	if (lock->holder >= 0)
57 		return;
58 
59 	rw_lock_waiter *waiter = lock->waiters;
60 	if (waiter == NULL)
61 		return;
62 
63 	if (waiter->writer) {
64 		if (lock->reader_count > 0)
65 			return;
66 
67 		lock->waiters = waiter->next;
68 		lock->holder = waiter->thread;
69 		_kern_unblock_thread(waiter->thread, B_OK);
70 		return;
71 	}
72 
73 	while (waiter != NULL && !waiter->writer) {
74 		lock->reader_count++;
75 		lock->waiters = waiter->next;
76 		_kern_unblock_thread(waiter->thread, B_OK);
77 		waiter = lock->waiters;
78 	}
79 }
80 
81 
82 void
83 rw_lock_init(rw_lock *lock, const char *name)
84 {
85 	rw_lock_init_etc(lock, name, 0);
86 }
87 
88 
89 void
90 rw_lock_init_etc(rw_lock *lock, const char *name, uint32 flags)
91 {
92 	lock->waiters = NULL;
93 	lock->holder = -1;
94 	lock->reader_count = 0;
95 	lock->writer_count = 0;
96 	lock->owner_count = 0;
97 	mutex_init_etc(&lock->lock, name, flags);
98 }
99 
100 
101 void
102 rw_lock_destroy(rw_lock *lock)
103 {
104 	mutex_lock(&lock->lock);
105 
106 	rw_lock_waiter *waiter = lock->waiters;
107 	while (waiter != NULL) {
108 		_kern_unblock_thread(waiter->thread, B_ERROR);
109 		waiter = waiter->next;
110 	}
111 
112 	mutex_destroy(&lock->lock);
113 }
114 
115 
116 status_t
117 rw_lock_read_lock(rw_lock *lock)
118 {
119 	MutexLocker locker(lock->lock);
120 
121 	if (lock->writer_count == 0) {
122 		lock->reader_count++;
123 		return B_OK;
124 	}
125 
126 	if (lock->holder == find_thread(NULL)) {
127 		lock->owner_count++;
128 		return B_OK;
129 	}
130 
131 	return rw_lock_wait(lock, false);
132 }
133 
134 
135 status_t
136 rw_lock_read_unlock(rw_lock *lock)
137 {
138 	MutexLocker locker(lock->lock);
139 
140 	if (lock->holder == find_thread(NULL)) {
141 		if (--lock->owner_count > 0)
142 			return B_OK;
143 
144 		// this originally has been a write lock
145 		lock->writer_count--;
146 		lock->holder = -1;
147 
148 		rw_lock_unblock(lock);
149 		return B_OK;
150 	}
151 
152 	if (lock->reader_count <= 0) {
153 		debugger("rw_lock not read locked");
154 		return B_ERROR;
155 	}
156 
157 	lock->reader_count--;
158 	rw_lock_unblock(lock);
159 	return B_OK;
160 }
161 
162 
163 status_t
164 rw_lock_write_lock(rw_lock *lock)
165 {
166 	MutexLocker locker(lock->lock);
167 
168 	if (lock->reader_count == 0 && lock->writer_count == 0) {
169 		lock->writer_count++;
170 		lock->holder = find_thread(NULL);
171 		lock->owner_count = 1;
172 		return B_OK;
173 	}
174 
175 	if (lock->holder == find_thread(NULL)) {
176 		lock->owner_count++;
177 		return B_OK;
178 	}
179 
180 	lock->writer_count++;
181 
182 	status_t result = rw_lock_wait(lock, true);
183 	if (result != B_OK)
184 		return result;
185 
186 	if (lock->holder != find_thread(NULL)) {
187 		debugger("write locked but holder not set");
188 		return B_ERROR;
189 	}
190 
191 	lock->owner_count = 1;
192 	return B_OK;
193 }
194 
195 
196 status_t
197 rw_lock_write_unlock(rw_lock *lock)
198 {
199 	MutexLocker locker(lock->lock);
200 
201 	if (lock->holder != find_thread(NULL)) {
202 		debugger("rw_lock not write locked");
203 		return B_ERROR;
204 	}
205 
206 	if (--lock->owner_count > 0)
207 		return B_OK;
208 
209 	lock->writer_count--;
210 	lock->holder = -1;
211 	rw_lock_unblock(lock);
212 	return B_OK;
213 }
214