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