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 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 |