Fix a few 'gcc -fanalyzer' warnings.
[pwmd.git] / src / mutex.h
blob088d751bc910db4b71fd478da70bc1c6924a12b2
1 /*
2 Copyright (C) 2006-2023 Ben Kibbey <bjk@luxsci.net>
4 This file is part of pwmd.
6 Pwmd is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 Pwmd is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
18 #ifndef MUTEX_H
19 #define MUTEX_H
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #ifdef HAVE_SYS_TIME_H
29 #include <sys/time.h>
30 #endif
31 #ifdef HAVE_SYS_FILE_H
32 #include <sys/file.h>
33 #endif
34 #ifdef HAVE_FCNTL_H
35 #include <fcntl.h>
36 #endif
37 #include <sys/select.h>
38 #include <sys/types.h>
39 #include <pthread.h>
40 #include <assert.h>
41 #include <errno.h>
42 #include <time.h>
43 #include "rcfile.h"
45 #ifndef HAVE_PTHREAD_CANCEL
46 extern pthread_key_t signal_thread_key;
48 #define TEST_CANCEL() do { \
49 int *cancel = (int *) pthread_getspecific (signal_thread_key); \
50 if (cancel && *cancel) \
51 pthread_exit (NULL); \
52 } while (0)
53 #else
54 #define TEST_CANCEL()
55 #endif
57 #define INIT_TIMESPEC(t, ts) do { \
58 long s = (t*100000000)/1000000000; \
59 long l = (t*100000000)%1000000000; \
60 clock_gettime(CLOCK_REALTIME, &ts); \
61 ts.tv_sec += s; \
62 if (ts.tv_nsec + l >= 1000000000) { \
63 ts.tv_sec++; \
64 ts.tv_nsec = 0; \
65 } \
66 else \
67 ts.tv_nsec += l; \
68 } while (0)
70 #ifdef MUTEX_DEBUG
71 #define MUTEX_LOCK_DEBUG(m) do { \
72 log_write("%s(%i): %s(): LOCK %p", __FILE__, __LINE__, \
73 __FUNCTION__, m); \
74 } while (0)
76 #define MUTEX_UNLOCK_DEBUG(m) do { \
77 log_write("%s(%i): %s(): UNLOCK %p", __FILE__, __LINE__, \
78 __FUNCTION__, m); \
79 } while (0)
80 #else
81 #define MUTEX_LOCK_DEBUG(m)
82 #define MUTEX_UNLOCK_DEBUG(m)
83 #endif
85 #define MUTEX_LOCK(m) do { \
86 MUTEX_LOCK_DEBUG(m); \
87 while (pthread_mutex_trylock (m) == EBUSY) { \
88 struct timeval tv = { 0, 3000 }; \
89 TEST_CANCEL (); \
90 select (0, NULL, NULL, NULL, &tv); \
91 }; \
92 } while (0)
94 #define MUTEX_UNLOCK(m) do { \
95 MUTEX_UNLOCK_DEBUG(m); \
96 pthread_mutex_unlock(m); \
97 } while (0)
99 #define TIMED_LOCK_DURATION 100000
100 #ifdef HAVE_FLOCK
101 #define TRY_FLOCK(ctx, fd, type, rc) do { \
102 long ka = (long)config_get_integer ("global", "keepalive_interval");\
103 long to = config_get_long ("global", "lock_timeout"); \
104 for (long elapsed = 0; ; elapsed++) { \
105 struct timeval tv = { 0, TIMED_LOCK_DURATION }; \
106 int n = flock (fd, type|LOCK_NB); \
107 if (n == -1 && errno == EWOULDBLOCK) { \
108 if (to == -1 || elapsed >= to) { \
109 rc = GPG_ERR_LOCKED; \
110 break; \
112 select (0, NULL, NULL, NULL, &tv); \
113 if (ctx && ka && elapsed && !((elapsed+1*10) % (ka*10))) { \
114 rc = send_status (ctx, STATUS_KEEPALIVE, NULL); \
115 if (rc) \
116 break; \
118 TEST_CANCEL(); \
120 else if (n == -1) \
121 rc = gpg_error_from_errno (errno); \
122 else \
123 break; \
125 } while (0)
126 #else
127 #define TRY_FLOCK(ctx, fd, type, rc) do { \
128 rc = 0; \
129 } while (0)
130 #endif
132 #define MUTEX_TIMED_LOCK(ctx, m, timeout, rc) do { \
133 long ka = (long)config_get_integer ("global", "keepalive_interval");\
134 for (long elapsed = 0; ; elapsed++) { \
135 struct timeval tv = { 0, TIMED_LOCK_DURATION }; \
136 int n = pthread_mutex_trylock (m); \
137 if (n == EBUSY) { \
138 if (timeout == -1 || elapsed >= timeout) { \
139 rc = GPG_ERR_LOCKED; \
140 break; \
142 select (0, NULL, NULL, NULL, &tv); \
143 if (ctx && ka && elapsed && !((elapsed+1*10) % (ka*10))) { \
144 rc = send_status (ctx, STATUS_KEEPALIVE, NULL); \
145 if (rc) \
146 break; \
148 TEST_CANCEL(); \
150 else if (n) { \
151 rc = gpg_error_from_errno (n); \
152 break; \
154 else { \
155 MUTEX_LOCK_DEBUG(m); \
156 break; \
159 } while (0)
161 #define MUTEX_TRYLOCK(ctx, m, rc, t) do { \
162 int err = pthread_mutex_trylock (m); \
163 rc = 0; \
164 if (err == EBUSY) { \
165 if (t != -1) { \
166 if (ctx) \
167 rc = send_status(ctx, STATUS_LOCKED, NULL); \
168 if (!rc && t != 0) \
169 MUTEX_TIMED_LOCK(ctx, m, t, rc); \
170 else if (!rc) \
171 rc = GPG_ERR_LOCKED; \
173 else \
174 rc = GPG_ERR_LOCKED; \
176 else if (err) \
177 rc = gpg_error_from_errno (err); \
178 else \
179 MUTEX_LOCK_DEBUG(m); \
180 } while (0)
182 #define MUTEX_TIMED_RWLOCK(ctx, m, timeout, rc, w) do { \
183 long ka = (long)config_get_integer ("global", "keepalive_interval");\
184 for (long elapsed = 0; ; elapsed++) { \
185 struct timeval tv = { 0, TIMED_LOCK_DURATION }; \
186 int err; \
187 if (w) \
188 err = pthread_rwlock_trywrlock(m); \
189 else \
190 err = pthread_rwlock_tryrdlock(m); \
191 if (err == EBUSY) { \
192 if (timeout == -1 || elapsed >= timeout) { \
193 rc = GPG_ERR_LOCKED; \
194 break; \
196 select (0, NULL, NULL, NULL, &tv); \
197 if (ctx && ka && elapsed && !((elapsed+1*10) % (ka*10))) { \
198 rc = send_status (ctx, STATUS_KEEPALIVE, NULL); \
199 if (rc) \
200 break; \
202 TEST_CANCEL(); \
204 else if (err) { \
205 rc = gpg_error_from_errno (err); \
206 break; \
208 else { \
209 MUTEX_LOCK_DEBUG(m); \
210 break; \
213 } while (0)
215 #endif