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
rw_lock_wait(rw_lock * lock,bool writer)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
rw_lock_unblock(rw_lock * lock)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
__rw_lock_init(rw_lock * lock,const char * name)83 __rw_lock_init(rw_lock *lock, const char *name)
84 {
85 rw_lock_init_etc(lock, name, 0);
86 }
87
88
89 void
__rw_lock_init_etc(rw_lock * lock,const char * name,uint32 flags)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
__rw_lock_destroy(rw_lock * lock)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
__rw_lock_read_lock(rw_lock * lock)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
__rw_lock_read_unlock(rw_lock * lock)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
__rw_lock_write_lock(rw_lock * lock)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
__rw_lock_write_unlock(rw_lock * lock)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