1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <assert.h>
#ifdef CONFIG_VALGRIND_DEV
#include <valgrind/valgrind.h>
#else
#define RUNNING_ON_VALGRIND 0
#endif
#include "fio_sem.h"
#include "pshared.h"
#include "os/os.h"
#include "fio_time.h"
#include "gettime.h"
void __fio_sem_remove(struct fio_sem *sem)
{
assert(sem->magic == FIO_SEM_MAGIC);
pthread_mutex_destroy(&sem->lock);
pthread_cond_destroy(&sem->cond);
/*
* When not running on Valgrind, ensure any subsequent attempt to grab
* this semaphore will fail with an assert, instead of just silently
* hanging. When running on Valgrind, let Valgrind detect
* use-after-free.
*/
if (!RUNNING_ON_VALGRIND)
memset(sem, 0, sizeof(*sem));
}
void fio_sem_remove(struct fio_sem *sem)
{
__fio_sem_remove(sem);
munmap((void *) sem, sizeof(*sem));
}
int __fio_sem_init(struct fio_sem *sem, int value)
{
int ret;
sem->value = value;
/* Initialize .waiters explicitly for Valgrind. */
sem->waiters = 0;
sem->magic = FIO_SEM_MAGIC;
ret = mutex_cond_init_pshared(&sem->lock, &sem->cond);
if (ret)
return ret;
return 0;
}
struct fio_sem *fio_sem_init(int value)
{
struct fio_sem *sem = NULL;
sem = (void *) mmap(NULL, sizeof(struct fio_sem),
PROT_READ | PROT_WRITE,
OS_MAP_ANON | MAP_SHARED, -1, 0);
if (sem == MAP_FAILED) {
perror("mmap semaphore");
return NULL;
}
if (!__fio_sem_init(sem, value))
return sem;
fio_sem_remove(sem);
return NULL;
}
static bool sem_timed_out(struct timespec *t, unsigned int msecs)
{
struct timeval tv;
struct timespec now;
gettimeofday(&tv, NULL);
now.tv_sec = tv.tv_sec;
now.tv_nsec = tv.tv_usec * 1000;
return mtime_since(t, &now) >= msecs;
}
int fio_sem_down_timeout(struct fio_sem *sem, unsigned int msecs)
{
struct timespec base;
struct timespec t;
int ret = 0;
assert(sem->magic == FIO_SEM_MAGIC);
#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK
clock_gettime(CLOCK_MONOTONIC, &t);
#else
clock_gettime(CLOCK_REALTIME, &t);
#endif
base = t;
t.tv_sec += msecs / 1000;
t.tv_nsec += ((msecs * 1000000ULL) % 1000000000);
if (t.tv_nsec >= 1000000000) {
t.tv_nsec -= 1000000000;
t.tv_sec++;
}
pthread_mutex_lock(&sem->lock);
sem->waiters++;
while (!sem->value && !ret) {
/*
* Some platforms (FreeBSD 9?) seems to return timed out
* way too early, double check.
*/
ret = pthread_cond_timedwait(&sem->cond, &sem->lock, &t);
if (ret == ETIMEDOUT && !sem_timed_out(&base, msecs))
ret = 0;
}
sem->waiters--;
if (!ret) {
sem->value--;
pthread_mutex_unlock(&sem->lock);
return 0;
}
pthread_mutex_unlock(&sem->lock);
return ret;
}
bool fio_sem_down_trylock(struct fio_sem *sem)
{
bool ret = true;
assert(sem->magic == FIO_SEM_MAGIC);
pthread_mutex_lock(&sem->lock);
if (sem->value) {
sem->value--;
ret = false;
}
pthread_mutex_unlock(&sem->lock);
return ret;
}
void fio_sem_down(struct fio_sem *sem)
{
assert(sem->magic == FIO_SEM_MAGIC);
pthread_mutex_lock(&sem->lock);
while (!sem->value) {
sem->waiters++;
pthread_cond_wait(&sem->cond, &sem->lock);
sem->waiters--;
}
sem->value--;
pthread_mutex_unlock(&sem->lock);
}
void fio_sem_up(struct fio_sem *sem)
{
int do_wake = 0;
assert(sem->magic == FIO_SEM_MAGIC);
pthread_mutex_lock(&sem->lock);
if (!sem->value && sem->waiters)
do_wake = 1;
sem->value++;
if (do_wake)
pthread_cond_signal(&sem->cond);
pthread_mutex_unlock(&sem->lock);
}
|