OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2014 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 /* | |
8 * Native Client rwlock implementation | |
9 * | |
10 * This implementation avoids writer starvation by preventing readers | |
Mark Seaborn
2014/12/01 22:33:07
You might want to comment on whether/why you want
Sam Clegg
2014/12/10 19:17:43
I added a link to the wikipedia page with more inf
| |
11 * from acquiring the lock while there are waiting writers. | |
12 * | |
13 * The thundering herd problem is avoided by only waking a single | |
14 * waiter (either a single writer or a single reader) when the | |
15 * lock is released. | |
16 */ | |
17 | |
18 #include <assert.h> | |
Mark Seaborn
2014/12/01 22:33:07
Not used
Sam Clegg
2014/12/10 19:17:42
Done.
| |
19 #include <errno.h> | |
20 #include <unistd.h> | |
21 | |
22 #include "native_client/src/include/nacl_compiler_annotations.h" | |
23 #include "native_client/src/untrusted/nacl/nacl_irt.h" | |
24 #include "native_client/src/untrusted/pthread/pthread.h" | |
25 #include "native_client/src/untrusted/pthread/pthread_internal.h" | |
26 #include "native_client/src/untrusted/pthread/pthread_types.h" | |
27 | |
28 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) { | |
29 attr->type = PTHREAD_PROCESS_PRIVATE; | |
30 return 0; | |
31 } | |
32 | |
33 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) { | |
34 return 0; | |
35 } | |
36 | |
37 int pthread_rwlock_init(pthread_rwlock_t *rwlock, | |
38 const pthread_rwlockattr_t *attr) { | |
39 rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID; | |
40 rwlock->writers_waiting = 0; | |
41 rwlock->reader_count = 0; | |
42 pthread_mutex_init(&rwlock->mutex, NULL); | |
Mark Seaborn
2014/12/01 22:33:07
You should really check for errors from all the pt
Sam Clegg
2014/12/10 19:17:42
Done.
| |
43 pthread_cond_init(&rwlock->write_possible, NULL); | |
44 pthread_cond_init(&rwlock->read_possible, NULL); | |
45 return 0; | |
46 } | |
47 | |
48 /** | |
Mark Seaborn
2014/12/01 22:33:07
Don't need "/**" here -- it's for doc comments, wh
Sam Clegg
2014/12/10 19:17:42
Done.
| |
49 * Helper function used by waiting writers to determine if they can take | |
50 * the lock. The rwlock->mutex must be held when calling this function. | |
51 * Returns 1 if the write lock can be taken, 0 it it can't. | |
52 */ | |
53 static inline int write_lock_available(pthread_rwlock_t *rwlock) { | |
54 /* | |
55 * write lock is available if there is no current writer and no current | |
Mark Seaborn
2014/12/01 22:33:07
Nit: capitalise as "Write"
Sam Clegg
2014/12/10 19:17:42
Done.
| |
56 * readers. | |
57 */ | |
58 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) | |
59 return 0; | |
60 if (rwlock->reader_count > 0) | |
61 return 0; | |
62 return 1; | |
63 } | |
64 | |
65 /** | |
66 * Helper function used by waiting readers to determine if they can take | |
67 * the lock. The rwlock->mutex must be held when calling this function. | |
68 * Returns 1 if the write lock can be taken, 0 it it can't. | |
69 */ | |
70 static inline int read_lock_available(pthread_rwlock_t *rwlock) { | |
71 /* | |
72 * read lock is available if there is no current writer and no *waiting* | |
Mark Seaborn
2014/12/01 22:33:07
Capitalise as "Read" too
Sam Clegg
2014/12/10 19:17:42
Done.
| |
73 * writers. | |
74 */ | |
75 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) | |
76 return 0; | |
77 if (rwlock->writers_waiting > 0) | |
78 return 0; | |
79 return 1; | |
80 } | |
81 | |
82 int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, | |
83 const struct timespec *abs_timeout) { | |
84 int rc = 0; | |
85 pthread_mutex_lock(&rwlock->mutex); | |
86 /* | |
87 * Wait repeatedly until the write preconditions are met. | |
88 * In theory this loop should only execute once because the preconditions | |
89 * should always be true when the condition is signaled. | |
90 */ | |
91 while (!read_lock_available(rwlock)) { | |
92 rc = pthread_cond_timedwait(&rwlock->read_possible, | |
93 &rwlock->mutex, | |
94 abs_timeout); | |
95 if (rc != 0) | |
96 goto done; | |
97 } | |
98 | |
99 /* Acquire the read lock. */ | |
100 rwlock->reader_count++; | |
101 done: | |
102 pthread_mutex_unlock(&rwlock->mutex); | |
103 return rc; | |
104 } | |
105 | |
106 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { | |
107 struct timespec timeout = { 0, 0 }; | |
Mark Seaborn
2014/12/01 22:33:07
This means a zero timeout, I think, so this won't
Sam Clegg
2014/12/10 19:17:42
Done.
| |
108 return pthread_rwlock_timedrdlock(rwlock, &timeout); | |
109 } | |
110 | |
111 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { | |
Mark Seaborn
2014/12/01 22:33:07
You *could* merge this into rwlock_lock(), using a
Sam Clegg
2014/12/10 19:17:43
Done.
| |
112 int rc = 0; | |
113 pthread_mutex_lock(&rwlock->mutex); | |
114 if (!read_lock_available(rwlock)) { | |
115 rc = EBUSY; | |
116 goto done; | |
117 } | |
118 | |
119 /* Acquire the read lock. */ | |
120 rwlock->reader_count++; | |
121 done: | |
122 pthread_mutex_unlock(&rwlock->mutex); | |
123 return rc; | |
124 } | |
125 | |
126 int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, | |
127 const struct timespec *abs_timeout) { | |
128 int rc = 0; | |
129 pthread_mutex_lock(&rwlock->mutex); | |
130 | |
131 /* Wait repeatedly until the write preconditions are met */ | |
132 while (!write_lock_available(rwlock)) { | |
133 /* | |
134 * Before waiting (and releasing the lock) we increment the | |
135 * waiting_writers count so the unlocking code knows to wake | |
136 * us first (before any waiting readers). | |
137 */ | |
138 rwlock->writers_waiting++; | |
139 rc = pthread_cond_timedwait(&rwlock->write_possible, | |
140 &rwlock->mutex, | |
141 abs_timeout); | |
142 rwlock->writers_waiting--; | |
143 if (rc != 0) | |
144 goto done; | |
145 } | |
146 | |
147 /* Acquire the write lock. */ | |
148 rwlock->writer_thread_id = pthread_self(); | |
149 done: | |
150 pthread_mutex_unlock(&rwlock->mutex); | |
151 return rc; | |
152 } | |
153 | |
154 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { | |
155 struct timespec timeout = { 0, 0 }; | |
156 return pthread_rwlock_timedwrlock(rwlock, &timeout); | |
157 } | |
158 | |
159 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { | |
160 int rc = 0; | |
161 pthread_mutex_lock(&rwlock->mutex); | |
162 /* Wait until there is no writer *and* no readers. */ | |
Mark Seaborn
2014/12/01 22:33:07
Nit: trywrlock() doesn't wait
Sam Clegg
2014/12/10 19:17:42
Done.
| |
163 if (!write_lock_available(rwlock)) { | |
164 rc = EBUSY; | |
165 goto done; | |
166 } | |
167 | |
168 /* Acquire the write lock. */ | |
169 rwlock->writer_thread_id = pthread_self(); | |
170 done: | |
171 pthread_mutex_unlock(&rwlock->mutex); | |
172 return rc; | |
173 } | |
174 | |
175 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { | |
176 int rc = 0; | |
177 pthread_mutex_lock(&rwlock->mutex); | |
178 | |
179 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) { | |
180 /* The write-lock is held. Ensure its us the holds it. */ | |
Mark Seaborn
2014/12/01 22:33:07
"the" -> "that"
Sam Clegg
2014/12/10 19:17:42
Done.
| |
181 if (rwlock->writer_thread_id != pthread_self()) { | |
182 rc = EPERM; | |
183 goto done; | |
184 } | |
185 | |
186 /* Release write lock. */ | |
187 rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID; | |
188 if (rwlock->writers_waiting > 0) { | |
189 /* Wake a waiting writer if there is one. */ | |
190 pthread_cond_signal(&rwlock->write_possible); | |
191 } else { | |
192 /* Otherwise wake a waiting reader. */ | |
193 pthread_cond_signal(&rwlock->read_possible); | |
194 } | |
195 } else { | |
196 if (rwlock->reader_count == 0) { | |
197 rc = EPERM; | |
198 goto done; | |
199 } | |
200 | |
201 /* Release read lock. */ | |
202 rwlock->reader_count--; | |
203 if (rwlock->reader_count == 0 && rwlock->writers_waiting > 0) { | |
204 /* Wake a waiting writer. */ | |
205 pthread_cond_signal(&rwlock->write_possible); | |
206 } | |
207 } | |
208 | |
209 done: | |
210 pthread_mutex_unlock(&rwlock->mutex); | |
211 return rc; | |
212 } | |
213 | |
214 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { | |
Mark Seaborn
2014/12/01 22:33:07
Optional: You could check that reader_count and wr
Sam Clegg
2014/12/10 19:17:43
Done.
| |
215 pthread_mutex_destroy(&rwlock->mutex); | |
216 pthread_cond_destroy(&rwlock->write_possible); | |
217 pthread_cond_destroy(&rwlock->read_possible); | |
218 return 0; | |
219 } | |
OLD | NEW |