1 #include <stdio.h> 2 #include <string.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <fcntl.h> 6 #include <time.h> 7 #include <errno.h> 8 #include <pthread.h> 9 #include <sys/mman.h> 10 #include <assert.h> 11 12 #include "fio.h" 13 #include "log.h" 14 #include "mutex.h" 15 #include "arch/arch.h" 16 #include "os/os.h" 17 #include "helpers.h" 18 #include "fio_time.h" 19 #include "gettime.h" 20 21 void __fio_mutex_remove(struct fio_mutex *mutex) 22 { 23 assert(mutex->magic == FIO_MUTEX_MAGIC); 24 pthread_cond_destroy(&mutex->cond); 25 } 26 27 void fio_mutex_remove(struct fio_mutex *mutex) 28 { 29 __fio_mutex_remove(mutex); 30 munmap((void *) mutex, sizeof(*mutex)); 31 } 32 33 int __fio_mutex_init(struct fio_mutex *mutex, int value) 34 { 35 pthread_mutexattr_t attr; 36 pthread_condattr_t cond; 37 int ret; 38 39 mutex->value = value; 40 mutex->magic = FIO_MUTEX_MAGIC; 41 42 ret = pthread_mutexattr_init(&attr); 43 if (ret) { 44 log_err("pthread_mutexattr_init: %s\n", strerror(ret)); 45 return ret; 46 } 47 48 /* 49 * Not all platforms support process shared mutexes (FreeBSD) 50 */ 51 #ifdef FIO_HAVE_PSHARED_MUTEX 52 ret = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); 53 if (ret) { 54 log_err("pthread_mutexattr_setpshared: %s\n", strerror(ret)); 55 return ret; 56 } 57 #endif 58 59 pthread_condattr_init(&cond); 60 #ifdef FIO_HAVE_PSHARED_MUTEX 61 pthread_condattr_setpshared(&cond, PTHREAD_PROCESS_SHARED); 62 #endif 63 pthread_cond_init(&mutex->cond, &cond); 64 65 ret = pthread_mutex_init(&mutex->lock, &attr); 66 if (ret) { 67 log_err("pthread_mutex_init: %s\n", strerror(ret)); 68 return ret; 69 } 70 71 pthread_condattr_destroy(&cond); 72 pthread_mutexattr_destroy(&attr); 73 return 0; 74 } 75 76 struct fio_mutex *fio_mutex_init(int value) 77 { 78 struct fio_mutex *mutex = NULL; 79 80 mutex = (void *) mmap(NULL, sizeof(struct fio_mutex), 81 PROT_READ | PROT_WRITE, 82 OS_MAP_ANON | MAP_SHARED, -1, 0); 83 if (mutex == MAP_FAILED) { 84 perror("mmap mutex"); 85 return NULL; 86 } 87 88 if (!__fio_mutex_init(mutex, value)) 89 return mutex; 90 91 fio_mutex_remove(mutex); 92 return NULL; 93 } 94 95 static int mutex_timed_out(struct timeval *t, unsigned int seconds) 96 { 97 return mtime_since_now(t) >= seconds * 1000; 98 } 99 100 int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int seconds) 101 { 102 struct timeval tv_s; 103 struct timespec t; 104 int ret = 0; 105 106 assert(mutex->magic == FIO_MUTEX_MAGIC); 107 108 gettimeofday(&tv_s, NULL); 109 t.tv_sec = tv_s.tv_sec + seconds; 110 t.tv_nsec = tv_s.tv_usec * 1000; 111 112 pthread_mutex_lock(&mutex->lock); 113 114 while (!mutex->value && !ret) { 115 mutex->waiters++; 116 117 /* 118 * Some platforms (FreeBSD 9?) seems to return timed out 119 * way too early, double check. 120 */ 121 ret = pthread_cond_timedwait(&mutex->cond, &mutex->lock, &t); 122 if (ret == ETIMEDOUT && !mutex_timed_out(&tv_s, seconds)) 123 ret = 0; 124 125 mutex->waiters--; 126 } 127 128 if (!ret) { 129 mutex->value--; 130 pthread_mutex_unlock(&mutex->lock); 131 } 132 133 return ret; 134 } 135 136 int fio_mutex_down_trylock(struct fio_mutex *mutex) 137 { 138 int ret = 1; 139 140 assert(mutex->magic == FIO_MUTEX_MAGIC); 141 142 pthread_mutex_lock(&mutex->lock); 143 if (mutex->value) { 144 mutex->value--; 145 ret = 0; 146 } 147 pthread_mutex_unlock(&mutex->lock); 148 149 return ret; 150 } 151 152 void fio_mutex_down(struct fio_mutex *mutex) 153 { 154 assert(mutex->magic == FIO_MUTEX_MAGIC); 155 156 pthread_mutex_lock(&mutex->lock); 157 158 while (!mutex->value) { 159 mutex->waiters++; 160 pthread_cond_wait(&mutex->cond, &mutex->lock); 161 mutex->waiters--; 162 } 163 164 mutex->value--; 165 pthread_mutex_unlock(&mutex->lock); 166 } 167 168 void fio_mutex_up(struct fio_mutex *mutex) 169 { 170 int do_wake = 0; 171 172 assert(mutex->magic == FIO_MUTEX_MAGIC); 173 174 pthread_mutex_lock(&mutex->lock); 175 read_barrier(); 176 if (!mutex->value && mutex->waiters) 177 do_wake = 1; 178 mutex->value++; 179 pthread_mutex_unlock(&mutex->lock); 180 181 if (do_wake) 182 pthread_cond_signal(&mutex->cond); 183 } 184 185 void fio_rwlock_write(struct fio_rwlock *lock) 186 { 187 assert(lock->magic == FIO_RWLOCK_MAGIC); 188 pthread_rwlock_wrlock(&lock->lock); 189 } 190 191 void fio_rwlock_read(struct fio_rwlock *lock) 192 { 193 assert(lock->magic == FIO_RWLOCK_MAGIC); 194 pthread_rwlock_rdlock(&lock->lock); 195 } 196 197 void fio_rwlock_unlock(struct fio_rwlock *lock) 198 { 199 assert(lock->magic == FIO_RWLOCK_MAGIC); 200 pthread_rwlock_unlock(&lock->lock); 201 } 202 203 void fio_rwlock_remove(struct fio_rwlock *lock) 204 { 205 assert(lock->magic == FIO_RWLOCK_MAGIC); 206 munmap((void *) lock, sizeof(*lock)); 207 } 208 209 struct fio_rwlock *fio_rwlock_init(void) 210 { 211 struct fio_rwlock *lock; 212 pthread_rwlockattr_t attr; 213 int ret; 214 215 lock = (void *) mmap(NULL, sizeof(struct fio_rwlock), 216 PROT_READ | PROT_WRITE, 217 OS_MAP_ANON | MAP_SHARED, -1, 0); 218 if (lock == MAP_FAILED) { 219 perror("mmap rwlock"); 220 lock = NULL; 221 goto err; 222 } 223 224 lock->magic = FIO_RWLOCK_MAGIC; 225 226 ret = pthread_rwlockattr_init(&attr); 227 if (ret) { 228 log_err("pthread_rwlockattr_init: %s\n", strerror(ret)); 229 goto err; 230 } 231 #ifdef FIO_HAVE_PSHARED_MUTEX 232 ret = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); 233 if (ret) { 234 log_err("pthread_rwlockattr_setpshared: %s\n", strerror(ret)); 235 goto destroy_attr; 236 } 237 238 ret = pthread_rwlock_init(&lock->lock, &attr); 239 #else 240 ret = pthread_rwlock_init(&lock->lock, NULL); 241 #endif 242 243 if (ret) { 244 log_err("pthread_rwlock_init: %s\n", strerror(ret)); 245 goto destroy_attr; 246 } 247 248 pthread_rwlockattr_destroy(&attr); 249 250 return lock; 251 destroy_attr: 252 pthread_rwlockattr_destroy(&attr); 253 err: 254 if (lock) 255 fio_rwlock_remove(lock); 256 return NULL; 257 } 258