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;
245  		sw = (struct qb_util_stopwatch *)calloc(1, sizeof(struct qb_util_stopwatch));
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
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
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
280  	qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw)
281  	{
282  		uint64_t e6;
(1) Event cond_true: Condition "sw->stopped == 0", taking true branch.
283  		if (sw->stopped == 0 || sw->started == 0) {
(2) Event return_constant: Returning constant "0f".
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