1 /*
2 * Copyright (C) 2010-2022 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Angus Salkeld <asalkeld@redhat.com>
7 *
8 * libqb is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * libqb is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with libqb. If not, see <http://www.gnu.org/licenses/>.
20 */
21 #include "os_base.h"
22
23 #include "util_int.h"
24 #include <pthread.h>
25 #include <sys/stat.h>
26 #include <qb/qbconfig.h>
27 #include <qb/qbdefs.h>
28 #include <qb/qbutil.h>
29
30 struct qb_thread_lock_s {
31 qb_thread_lock_type_t type;
32 #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
33 pthread_spinlock_t spinlock;
34 #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
35 pthread_mutex_t mutex;
36 };
37
38 qb_thread_lock_t *
39 qb_thread_lock_create(qb_thread_lock_type_t type)
40 {
41 struct qb_thread_lock_s *tl = malloc(sizeof(struct qb_thread_lock_s));
42 int32_t res;
43
44 if (tl == NULL) {
45 return NULL;
46 }
47 #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
48 if (type == QB_THREAD_LOCK_SHORT) {
49 tl->type = QB_THREAD_LOCK_SHORT;
50 res = pthread_spin_init(&tl->spinlock, 1);
51 } else
52 #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
53 {
54 tl->type = QB_THREAD_LOCK_LONG;
55 res = pthread_mutex_init(&tl->mutex, NULL);
56 }
57 if (res == 0) {
58 return tl;
59 } else {
60 free(tl);
61 return NULL;
62 }
63 }
64
65 int32_t
66 qb_thread_lock(qb_thread_lock_t * tl)
67 {
68 int32_t res;
69 #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
70 if (tl->type == QB_THREAD_LOCK_SHORT) {
71 res = -pthread_spin_lock(&tl->spinlock);
72 } else
73 #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
74 {
75 res = -pthread_mutex_lock(&tl->mutex);
76 }
77 return res;
78 }
79
80 int32_t
81 qb_thread_unlock(qb_thread_lock_t * tl)
82 {
83 int32_t res;
84 #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
85 if (tl->type == QB_THREAD_LOCK_SHORT) {
86 res = -pthread_spin_unlock(&tl->spinlock);
87 } else
88 #endif
89 {
90 res = -pthread_mutex_unlock(&tl->mutex);
91 }
92 return res;
93 }
94
95 int32_t
96 qb_thread_trylock(qb_thread_lock_t * tl)
97 {
98 int32_t res;
99 #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
100 if (tl->type == QB_THREAD_LOCK_SHORT) {
101 res = -pthread_spin_trylock(&tl->spinlock);
102 } else
103 #endif
104 {
105 res = -pthread_mutex_trylock(&tl->mutex);
106 }
107 return res;
108 }
109
110 int32_t
111 qb_thread_lock_destroy(qb_thread_lock_t * tl)
112 {
113 int32_t res;
114 #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
115 if (tl->type == QB_THREAD_LOCK_SHORT) {
116 res = -pthread_spin_destroy(&tl->spinlock);
117 } else
118 #endif
119 {
120 res = -pthread_mutex_destroy(&tl->mutex);
121 }
122 free(tl);
123 return res;
124 }
125
126 /* If the "coarse" monotonic clock is available, it provides the time as of the
127 * last clock tick (typically at millisecond resolution), which should be
128 * sufficient for expected uses of libqb. This is faster since it avoids a
129 * context switch to the kernel.
130 */
131 #ifdef HAVE_MONOTONIC_CLOCK
132 # ifdef CLOCK_REALTIME_COARSE
133 # define QB_CLOCK_REALTIME CLOCK_REALTIME_COARSE
134 # else
135 # define QB_CLOCK_REALTIME CLOCK_REALTIME
136 # endif
137 #endif
138
139 void
140 qb_timespec_add_ms(struct timespec *ts, int32_t ms)
141 {
142 #ifndef S_SPLINT_S
143 ts->tv_sec += ms / 1000;
144 ts->tv_nsec += (ms % 1000) * QB_TIME_NS_IN_MSEC;
145 if (ts->tv_nsec >= 1000000000L) {
146 ts->tv_sec++;
147 ts->tv_nsec = ts->tv_nsec - 1000000000L;
148 }
149 #endif /* S_SPLINT_S */
150 }
151
152 uint64_t
153 qb_util_nano_current_get(void)
154 {
155 #ifdef HAVE_MONOTONIC_CLOCK
156 struct timespec ts;
157
158 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
159 return (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec;
160 }
161 #endif
162 return qb_util_nano_from_epoch_get();
163 }
164
165 uint64_t
166 qb_util_nano_from_epoch_get(void)
167 {
168 #ifdef HAVE_MONOTONIC_CLOCK
169 struct timespec ts;
170
171 if (clock_gettime(QB_CLOCK_REALTIME, &ts) == 0) {
172 return (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec;
173 }
174 #endif
175
176 #ifdef HAVE_GETTIMEOFDAY
177 {
178 struct timeval time_from_epoch;
179
180 if (gettimeofday(&time_from_epoch, NULL) == 0) {
181 return (time_from_epoch.tv_sec * QB_TIME_NS_IN_SEC) +
182 (time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC);
183 }
184 }
185 #endif
186
187 return time(NULL) * QB_TIME_NS_IN_SEC;
188 }
189
190 uint64_t
191 qb_util_nano_monotonic_hz(void)
192 {
193 #ifdef HAVE_MONOTONIC_CLOCK
194 struct timespec ts;
195
196 if ((clock_getres(CLOCK_MONOTONIC, &ts) == 0)
197 || (clock_getres(CLOCK_REALTIME, &ts) == 0)) {
198 return QB_TIME_NS_IN_SEC / (ts.tv_sec * QB_TIME_NS_IN_SEC + ts.tv_nsec);
199 }
200 #endif
201
202 return sysconf(_SC_CLK_TCK);
203 }
204
205 void
206 qb_util_timespec_from_epoch_get(struct timespec *ts)
207 {
208 #ifdef HAVE_MONOTONIC_CLOCK
209 if (clock_gettime(QB_CLOCK_REALTIME, ts) == 0) {
210 return;
211 }
212 #endif
213
214 #ifdef HAVE_GETTIMEOFDAY
215 {
216 struct timeval time_from_epoch;
217
218 if (gettimeofday(&time_from_epoch, NULL) == 0) {
219 #ifndef S_SPLINT_S
220 ts->tv_sec = time_from_epoch.tv_sec;
221 ts->tv_nsec = time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC;
222 #endif /* S_SPLINT_S */
223 return;
224 }
225 }
226 #endif
227
228 ts->tv_sec = time(NULL);
229 ts->tv_nsec = 0;
230 }
231
232 struct qb_util_stopwatch {
233 uint64_t started;
234 uint64_t stopped;
235 uint32_t split_options;
236 uint32_t split_size;
237 uint32_t split_entries;
238 uint64_t *split_entry_list;
239 };
240
241 qb_util_stopwatch_t *
242 qb_util_stopwatch_create(void)
243 {
244 struct qb_util_stopwatch *sw;
|
(1) Event alloc_fn: |
Storage is returned from allocation function "calloc". |
|
(2) Event assign: |
Assigning: "sw" = "(struct qb_util_stopwatch *)calloc(1UL, 40UL)". |
| Also see events: |
[return_alloc] |
245 sw = (struct qb_util_stopwatch *)calloc(1, sizeof(struct qb_util_stopwatch));
|
(3) Event return_alloc: |
Returning allocated memory "sw". |
| Also see events: |
[alloc_fn][assign] |
246 return sw;
247 }
248
249 void
250 qb_util_stopwatch_free(qb_util_stopwatch_t * sw)
251 {
252 free(sw->split_entry_list);
253 free(sw);
254 }
255
256 void
|
(1) Event noescape: |
"qb_util_stopwatch_start(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_start(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_start(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_start(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
257 qb_util_stopwatch_start(qb_util_stopwatch_t * sw)
258 {
259 sw->started = qb_util_nano_current_get();
260 sw->stopped = 0;
261 sw->split_entries = 0;
262 }
263
264 void
|
(1) Event noescape: |
"qb_util_stopwatch_stop(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_stop(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_stop(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_stop(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
265 qb_util_stopwatch_stop(qb_util_stopwatch_t * sw)
266 {
267 sw->stopped = qb_util_nano_current_get();
268 }
269
270 uint64_t
271 qb_util_stopwatch_us_elapsed_get(qb_util_stopwatch_t * sw)
272 {
273 if (sw->stopped == 0 || sw->started == 0) {
274 return 0;
275 }
276 return ((sw->stopped - sw->started) / QB_TIME_NS_IN_USEC);
277 }
278
279 float
|
(1) Event noescape: |
"qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
|
(1) Event noescape: |
"qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t *)" does not free or save its parameter "sw". |
280 qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw)
281 {
282 uint64_t e6;
283 if (sw->stopped == 0 || sw->started == 0) {
284 return 0;
285 }
286 e6 = qb_util_stopwatch_us_elapsed_get(sw);
287 return ((float)e6 / (float)QB_TIME_US_IN_SEC);
288 }
289
290 int32_t
291 qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw,
292 uint32_t max_splits, uint32_t options)
293 {
294 sw->split_size = max_splits;
295 sw->split_options = options;
296 sw->split_entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * max_splits);
297 if (sw->split_entry_list == NULL) {
298 return -errno;
299 }
300 return 0;
301 }
302
303 uint64_t
304 qb_util_stopwatch_split(qb_util_stopwatch_t *sw)
305 {
306 uint32_t new_entry_pos;
307 uint64_t time_start;
308 uint64_t time_end;
309
310 if (sw->split_size == 0) {
311 return 0;
312 }
313 if (!(sw->split_options & QB_UTIL_SW_OVERWRITE) &&
314 sw->split_entries == sw->split_size) {
315 return 0;
316 }
317 if (sw->started == 0) {
318 qb_util_stopwatch_start(sw);
319 }
320 new_entry_pos = sw->split_entries % (sw->split_size);
321 sw->split_entry_list[new_entry_pos] = qb_util_nano_current_get();
322 sw->split_entries++;
323
324 time_start = sw->split_entry_list[new_entry_pos];
325 if (sw->split_entries == 1) {
326 /* first entry */
327 time_end = sw->started;
328 } else if (new_entry_pos == 0) {
329 /* wrap around */
330 time_end = sw->split_entry_list[sw->split_size - 1];
331 } else {
332 time_end = sw->split_entry_list[(new_entry_pos - 1) % sw->split_size];
333 }
334 return (time_start - time_end) / QB_TIME_NS_IN_USEC;
335 }
336
337 uint32_t
338 qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw)
339 {
340 if (sw->split_entries) {
341 return sw->split_entries - 1;
342 }
343 return sw->split_entries;
344 }
345
346 uint64_t
347 qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw,
348 uint32_t receint, uint32_t older)
349 {
350 uint64_t time_start;
351 uint64_t time_end;
352
353 if (sw->started == 0 ||
354 receint >= sw->split_entries ||
355 older >= sw->split_entries ||
356 receint < older) {
357 return 0;
358 }
359 if (sw->split_options & QB_UTIL_SW_OVERWRITE &&
360 (receint < (sw->split_entries - sw->split_size) ||
361 older < (sw->split_entries - sw->split_size))) {
362 return 0;
363 }
364
365 time_start = sw->split_entry_list[receint % (sw->split_size)];
366 if (older == receint) {
367 time_end = sw->started;
368 } else {
369 time_end = sw->split_entry_list[older % (sw->split_size)];
370 }
371 return (time_start - time_end) / QB_TIME_NS_IN_USEC;
372 }
373
374 const struct qb_version qb_ver = {
375 .major = QB_VER_MAJOR,
376 .minor = QB_VER_MINOR,
377 .micro = QB_VER_MICRO,
378 .rest = QB_VER_REST,
379 };
380
381 const char *const qb_ver_str = QB_VER_STR;
382