xref: /haiku/src/system/libroot/os/locks/rw_lock.cpp (revision 3c6e2dd68577c34d93e17f19711f6245bf6d0915)
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 status_t
83 rw_lock_init(rw_lock *lock, const char *name)
84 {
85 	lock->name = name;
86 	lock->waiters = NULL;
87 	lock->holder = -1;
88 	lock->reader_count = 0;
89 	lock->writer_count = 0;
90 	lock->owner_count = 0;
91 	return mutex_init(&lock->lock, name);
92 }
93 
94 
95 void
96 rw_lock_destroy(rw_lock *lock)
97 {
98 	mutex_lock(&lock->lock);
99 
100 	rw_lock_waiter *waiter = lock->waiters;
101 	while (waiter != NULL) {
102 		_kern_unblock_thread(waiter->thread, B_ERROR);
103 		waiter = waiter->next;
104 	}
105 
106 	mutex_destroy(&lock->lock);
107 }
108 
109 
110 status_t
111 rw_lock_read_lock(rw_lock *lock)
112 {
113 	MutexLocker locker(lock->lock);
114 
115 	if (lock->writer_count == 0) {
116 		lock->reader_count++;
117 		return B_OK;
118 	}
119 
120 	if (lock->holder == find_thread(NULL)) {
121 		lock->owner_count++;
122 		return B_OK;
123 	}
124 
125 	return rw_lock_wait(lock, false);
126 }
127 
128 
129 status_t
130 rw_lock_read_unlock(rw_lock *lock)
131 {
132 	MutexLocker locker(lock->lock);
133 
134 	if (lock->holder == find_thread(NULL)) {
135 		if (--lock->owner_count > 0)
136 			return B_OK;
137 
138 		// this originally has been a write lock
139 		lock->writer_count--;
140 		lock->holder = -1;
141 
142 		rw_lock_unblock(lock);
143 		return B_OK;
144 	}
145 
146 	if (lock->reader_count <= 0) {
147 		debugger("rw_lock not read locked");
148 		return B_ERROR;
149 	}
150 
151 	lock->reader_count--;
152 	rw_lock_unblock(lock);
153 	return B_OK;
154 }
155 
156 
157 status_t
158 rw_lock_write_lock(rw_lock *lock)
159 {
160 	MutexLocker locker(lock->lock);
161 
162 	if (lock->reader_count == 0 && lock->writer_count == 0) {
163 		lock->writer_count++;
164 		lock->holder = find_thread(NULL);
165 		lock->owner_count = 1;
166 		return B_OK;
167 	}
168 
169 	if (lock->holder == find_thread(NULL)) {
170 		lock->owner_count++;
171 		return B_OK;
172 	}
173 
174 	lock->writer_count++;
175 
176 	status_t result = rw_lock_wait(lock, true);
177 	if (result != B_OK)
178 		return result;
179 
180 	if (lock->holder != find_thread(NULL)) {
181 		debugger("write locked but holder not set");
182 		return B_ERROR;
183 	}
184 
185 	lock->owner_count = 1;
186 	return B_OK;
187 }
188 
189 
190 status_t
191 rw_lock_write_unlock(rw_lock *lock)
192 {
193 	MutexLocker locker(lock->lock);
194 
195 	if (lock->holder != find_thread(NULL)) {
196 		debugger("rw_lock not write locked");
197 		return B_ERROR;
198 	}
199 
200 	if (--lock->owner_count > 0)
201 		return B_OK;
202 
203 	lock->writer_count--;
204 	lock->holder = -1;
205 	rw_lock_unblock(lock);
206 	return B_OK;
207 }
208