Home | History | Annotate | Download | only in fio
      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