Chromium Code Reviews| 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 is a 'write-prefering' reader-writer lock which | |
|
Mark Seaborn
2015/01/05 16:58:53
"preferring"
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 11 * avoids writer starvation by preventing readers from acquiring the lock | |
| 12 * while there are waiting writers. See: | |
| 13 * (http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) | |
|
Mark Seaborn
2015/01/05 16:58:53
Nit: remove () brackets
Sam Clegg
2015/01/06 19:06:25
Done.
| |
| 14 * | |
| 15 * The thundering herd problem is avoided by only waking a single | |
| 16 * waiter (either a single writer or a single reader) when the | |
| 17 * lock is released. | |
| 18 */ | |
| 19 | |
| 20 #include <errno.h> | |
| 21 #include <unistd.h> | |
|
Mark Seaborn
2015/01/05 16:58:53
Is this used?
Sam Clegg
2015/01/06 19:06:25
Done.
| |
| 22 | |
| 23 #include "native_client/src/include/nacl_compiler_annotations.h" | |
|
Mark Seaborn
2015/01/05 16:58:53
Is this used?
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 24 #include "native_client/src/untrusted/nacl/nacl_irt.h" | |
|
Mark Seaborn
2015/01/05 16:58:53
Nit: Not used, AFAICT
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 25 #include "native_client/src/untrusted/pthread/pthread.h" | |
| 26 #include "native_client/src/untrusted/pthread/pthread_internal.h" | |
| 27 #include "native_client/src/untrusted/pthread/pthread_types.h" | |
| 28 | |
| 29 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) { | |
| 30 attr->type = PTHREAD_PROCESS_PRIVATE; | |
| 31 return 0; | |
| 32 } | |
| 33 | |
| 34 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr) { | |
| 35 return 0; | |
| 36 } | |
| 37 | |
| 38 int pthread_rwlock_init(pthread_rwlock_t *rwlock, | |
| 39 const pthread_rwlockattr_t *attr) { | |
| 40 rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID; | |
| 41 rwlock->writers_waiting = 0; | |
| 42 rwlock->reader_count = 0; | |
| 43 int rc = pthread_mutex_init(&rwlock->mutex, NULL); | |
| 44 if (rc != 0) | |
| 45 return rc; | |
| 46 rc = pthread_cond_init(&rwlock->write_possible, NULL); | |
| 47 if (rc != 0) | |
| 48 return rc; | |
| 49 return pthread_cond_init(&rwlock->read_possible, NULL); | |
| 50 } | |
| 51 | |
| 52 /* | |
| 53 * Helper function used by waiting writers to determine if they can take | |
| 54 * the lock. The rwlock->mutex must be held when calling this function. | |
| 55 * Returns 1 if the write lock can be taken, 0 it it can't. | |
|
Mark Seaborn
2015/01/05 16:58:53
"if it"
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 56 */ | |
| 57 static inline int write_lock_available(pthread_rwlock_t *rwlock) { | |
| 58 /* | |
| 59 * Write lock is available if there is no current writer and no current | |
| 60 * readers. | |
| 61 */ | |
| 62 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) | |
| 63 return 0; | |
| 64 if (rwlock->reader_count > 0) | |
| 65 return 0; | |
| 66 return 1; | |
| 67 } | |
| 68 | |
| 69 /* | |
| 70 * Helper function used by waiting readers to determine if they can take | |
| 71 * the lock. The rwlock->mutex must be held when calling this function. | |
| 72 * Returns 1 if the write lock can be taken, 0 it it can't. | |
|
Mark Seaborn
2015/01/05 16:58:53
Ditto
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 73 */ | |
| 74 static inline int read_lock_available(pthread_rwlock_t *rwlock) { | |
| 75 /* | |
| 76 * Read lock is available if there is no current writer and no *waiting* | |
| 77 * writers. | |
| 78 */ | |
| 79 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) | |
| 80 return 0; | |
| 81 if (rwlock->writers_waiting > 0) | |
| 82 return 0; | |
| 83 return 1; | |
| 84 } | |
| 85 | |
| 86 /* | |
| 87 * Internal function used to acquire the read lock. | |
| 88 * This operates is three differnet ways in order to implement the three public | |
|
Mark Seaborn
2015/01/05 16:58:53
"operates in", "different"
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 89 * functions: | |
| 90 * pthread_rwlock_rdlock | |
| 91 * pthread_rwlock_tryrdlock | |
| 92 * pthread_rwlock_timedrdlock | |
| 93 */ | |
| 94 static int rdlock_internal(pthread_rwlock_t *rwlock, | |
| 95 const struct timespec *abs_timeout, | |
| 96 int try_only) { | |
| 97 int rc = pthread_mutex_lock(&rwlock->mutex); | |
| 98 if (rc != 0) | |
| 99 return rc; | |
| 100 | |
| 101 /* | |
| 102 * Wait repeatedly until the write preconditions are met. | |
| 103 * In theory this loop should only execute once because the preconditions | |
| 104 * should always be true when the condition is signaled. | |
| 105 */ | |
| 106 while (!read_lock_available(rwlock)) { | |
| 107 if (try_only) { | |
| 108 rc = EBUSY; | |
| 109 } else if (abs_timeout != NULL) { | |
| 110 rc = pthread_cond_timedwait(&rwlock->read_possible, | |
| 111 &rwlock->mutex, | |
| 112 abs_timeout); | |
| 113 | |
|
Mark Seaborn
2015/01/05 16:58:53
Remove empty line
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 114 } else { | |
| 115 rc = pthread_cond_wait(&rwlock->read_possible, &rwlock->mutex); | |
| 116 } | |
| 117 if (rc != 0) | |
| 118 goto done; | |
| 119 } | |
| 120 | |
| 121 /* Acquire the read lock. */ | |
| 122 rwlock->reader_count++; | |
| 123 done: | |
| 124 pthread_mutex_unlock(&rwlock->mutex); | |
| 125 return rc; | |
| 126 } | |
| 127 | |
| 128 /* | |
| 129 * Internal function used to acquire the write lock. | |
| 130 * This operates is three differnet ways in order to implement the three public | |
|
Mark Seaborn
2015/01/05 16:58:53
Same here
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 131 * functions: | |
| 132 * pthread_rwlock_wrlock | |
| 133 * pthread_rwlock_trywrlock | |
| 134 * pthread_rwlock_timedwrlock | |
| 135 */ | |
| 136 static int rwlock_internal(pthread_rwlock_t *rwlock, | |
| 137 const struct timespec *abs_timeout, | |
| 138 int try_only) { | |
| 139 int rc = pthread_mutex_lock(&rwlock->mutex); | |
| 140 if (rc != 0) | |
| 141 return rc; | |
| 142 | |
| 143 /* Wait repeatedly until the write preconditions are met */ | |
| 144 while (!write_lock_available(rwlock)) { | |
| 145 if (try_only) { | |
| 146 rc = EBUSY; | |
| 147 } else { | |
| 148 /* | |
| 149 * Before waiting (and releasing the lock) we increment the | |
| 150 * waiting_writers count so the unlocking code knows to wake | |
| 151 * us first (before any waiting readers). | |
| 152 */ | |
| 153 rwlock->writers_waiting++; | |
| 154 if (abs_timeout) { | |
|
Mark Seaborn
2015/01/05 16:58:53
Style nit: Add "!= NULL"
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 155 rc = pthread_cond_timedwait(&rwlock->write_possible, | |
| 156 &rwlock->mutex, | |
| 157 abs_timeout); | |
| 158 } else { | |
| 159 rc = pthread_cond_wait(&rwlock->write_possible, | |
| 160 &rwlock->mutex); | |
| 161 } | |
| 162 rwlock->writers_waiting--; | |
| 163 } | |
| 164 if (rc != 0) | |
| 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); | |
|
Mark Seaborn
2015/01/05 16:58:53
You should really check the return value here too.
Sam Clegg
2015/01/06 19:06:25
Done.
| |
| 172 return rc; | |
| 173 } | |
| 174 | |
| 175 int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, | |
| 176 const struct timespec *abs_timeout) { | |
| 177 return rdlock_internal(rwlock, abs_timeout, 0); | |
| 178 } | |
| 179 | |
| 180 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { | |
| 181 return rdlock_internal(rwlock, NULL, 0); | |
| 182 } | |
| 183 | |
| 184 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { | |
| 185 return rdlock_internal(rwlock, NULL, 1); | |
| 186 } | |
| 187 | |
| 188 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { | |
| 189 return rwlock_internal(rwlock, NULL, 0); | |
| 190 } | |
| 191 | |
| 192 int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, | |
| 193 const struct timespec *abs_timeout) { | |
| 194 return rwlock_internal(rwlock, abs_timeout, 0); | |
| 195 } | |
| 196 | |
| 197 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { | |
| 198 return rwlock_internal(rwlock, NULL, 1); | |
| 199 } | |
| 200 | |
| 201 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { | |
| 202 int rc = pthread_mutex_lock(&rwlock->mutex); | |
| 203 if (rc != 0) | |
| 204 return rc; | |
| 205 | |
| 206 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) { | |
| 207 /* The write lock is held. Ensure its us that holds it. */ | |
|
Mark Seaborn
2015/01/05 16:58:53
"it's"
"us" is vague. How about "Ensure it's the
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 208 if (rwlock->writer_thread_id != pthread_self()) { | |
| 209 rc = EPERM; | |
| 210 goto done; | |
| 211 } | |
| 212 | |
| 213 /* Release write lock. */ | |
| 214 rwlock->writer_thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID; | |
| 215 if (rwlock->writers_waiting > 0) { | |
| 216 /* Wake a waiting writer if there is one. */ | |
| 217 rc = pthread_cond_signal(&rwlock->write_possible); | |
| 218 } else { | |
| 219 /* Otherwise wake a waiting reader. */ | |
| 220 rc = pthread_cond_signal(&rwlock->read_possible); | |
| 221 } | |
| 222 } else { | |
| 223 if (rwlock->reader_count == 0) { | |
| 224 rc = EPERM; | |
| 225 goto done; | |
| 226 } | |
| 227 | |
| 228 /* Release read lock. */ | |
| 229 rwlock->reader_count--; | |
| 230 if (rwlock->reader_count == 0 && rwlock->writers_waiting > 0) { | |
| 231 /* Wake a waiting writer. */ | |
| 232 rc = pthread_cond_signal(&rwlock->write_possible); | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 done: | |
| 237 pthread_mutex_unlock(&rwlock->mutex); | |
|
Mark Seaborn
2015/01/05 16:58:53
Ditto: check return value
Sam Clegg
2015/01/06 19:06:25
Done.
| |
| 238 return rc; | |
| 239 } | |
| 240 | |
| 241 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { | |
| 242 /* Return EBUSY if anther thread hold the mutex */ | |
|
Mark Seaborn
2015/01/05 16:58:53
"another", "holds". (Can you proof-read your comm
Sam Clegg
2015/01/06 19:06:24
Done.
| |
| 243 int rc = pthread_mutex_trylock(&rwlock->mutex); | |
| 244 if (rc != 0) { | |
| 245 return rc; | |
| 246 } | |
| 247 | |
| 248 /* Return EBUSY if there are current readers or a current writer */ | |
| 249 if (rwlock->reader_count != 0) { | |
| 250 pthread_mutex_unlock(&rwlock->mutex); | |
| 251 return EBUSY; | |
| 252 } | |
| 253 if (rwlock->writer_thread_id != NACL_PTHREAD_ILLEGAL_THREAD_ID) { | |
| 254 pthread_mutex_unlock(&rwlock->mutex); | |
| 255 return EBUSY; | |
| 256 } | |
| 257 | |
| 258 int rc1 = pthread_cond_destroy(&rwlock->write_possible); | |
| 259 int rc2 = pthread_cond_destroy(&rwlock->read_possible); | |
| 260 | |
| 261 /* Finally unlock the mutex and destroy it */ | |
| 262 pthread_mutex_unlock(&rwlock->mutex); | |
| 263 int rc3 = pthread_mutex_destroy(&rwlock->mutex); | |
| 264 if (rc1 != 0) | |
| 265 return rc1; | |
| 266 if (rc2 != 0) | |
| 267 return rc2; | |
| 268 return rc3; | |
| 269 } | |
| OLD | NEW |