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