diff --git a/lib/util.c b/lib/util.c index ed09313..320ea3e 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,380 +1,381 @@ /* * Copyright (C) 2010-2022 Red Hat, Inc. * * All rights reserved. * * Author: Angus Salkeld * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include "util_int.h" #include #include #include #include #include struct qb_thread_lock_s { qb_thread_lock_type_t type; #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK pthread_spinlock_t spinlock; #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */ pthread_mutex_t mutex; }; qb_thread_lock_t * qb_thread_lock_create(qb_thread_lock_type_t type) { struct qb_thread_lock_s *tl = malloc(sizeof(struct qb_thread_lock_s)); int32_t res; if (tl == NULL) { return NULL; } #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK if (type == QB_THREAD_LOCK_SHORT) { tl->type = QB_THREAD_LOCK_SHORT; res = pthread_spin_init(&tl->spinlock, 1); } else #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */ { tl->type = QB_THREAD_LOCK_LONG; res = pthread_mutex_init(&tl->mutex, NULL); } if (res == 0) { return tl; } else { free(tl); return NULL; } } int32_t qb_thread_lock(qb_thread_lock_t * tl) { int32_t res; #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK if (tl->type == QB_THREAD_LOCK_SHORT) { res = -pthread_spin_lock(&tl->spinlock); } else #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */ { res = -pthread_mutex_lock(&tl->mutex); } return res; } int32_t qb_thread_unlock(qb_thread_lock_t * tl) { int32_t res; #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK if (tl->type == QB_THREAD_LOCK_SHORT) { res = -pthread_spin_unlock(&tl->spinlock); } else #endif { res = -pthread_mutex_unlock(&tl->mutex); } return res; } int32_t qb_thread_trylock(qb_thread_lock_t * tl) { int32_t res; #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK if (tl->type == QB_THREAD_LOCK_SHORT) { res = -pthread_spin_trylock(&tl->spinlock); } else #endif { res = -pthread_mutex_trylock(&tl->mutex); } return res; } int32_t qb_thread_lock_destroy(qb_thread_lock_t * tl) { int32_t res; #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK if (tl->type == QB_THREAD_LOCK_SHORT) { res = -pthread_spin_destroy(&tl->spinlock); } else #endif { res = -pthread_mutex_destroy(&tl->mutex); } free(tl); return res; } /* If the "coarse" monotonic clock is available, it provides the time as of the * last clock tick (typically at millisecond resolution), which should be * sufficient for expected uses of libqb. This is faster since it avoids a * context switch to the kernel. */ #ifdef HAVE_MONOTONIC_CLOCK # ifdef CLOCK_REALTIME_COARSE # define QB_CLOCK_REALTIME CLOCK_REALTIME_COARSE # else # define QB_CLOCK_REALTIME CLOCK_REALTIME # endif #endif void qb_timespec_add_ms(struct timespec *ts, int32_t ms) { #ifndef S_SPLINT_S ts->tv_sec += ms / 1000; ts->tv_nsec += (ms % 1000) * QB_TIME_NS_IN_MSEC; if (ts->tv_nsec >= 1000000000L) { ts->tv_sec++; ts->tv_nsec = ts->tv_nsec - 1000000000L; } #endif /* S_SPLINT_S */ } uint64_t qb_util_nano_current_get(void) { #ifdef HAVE_MONOTONIC_CLOCK - uint64_t nano_monotonic; struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - nano_monotonic = - (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec; - return (nano_monotonic); -#else + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + return (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec; + } +#endif return qb_util_nano_from_epoch_get(); -#endif /* HAVE_MONOTONIC_CLOCK */ } uint64_t qb_util_nano_from_epoch_get(void) { #ifdef HAVE_MONOTONIC_CLOCK - uint64_t nano_monotonic; struct timespec ts; - clock_gettime(QB_CLOCK_REALTIME, &ts); - nano_monotonic = - (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec; - return (nano_monotonic); -#elif defined(HAVE_GETTIMEOFDAY) - uint64_t nano_from_epoch; - struct timeval time_from_epoch; - gettimeofday(&time_from_epoch, 0); - - nano_from_epoch = ((time_from_epoch.tv_sec * QB_TIME_NS_IN_SEC) + - (time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC)); - - return (nano_from_epoch); -#else -#error No high-resolution clock available -#endif /* HAVE_MONOTONIC_CLOCK */ + + if (clock_gettime(QB_CLOCK_REALTIME, &ts) == 0) { + return (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec; + } +#endif + +#ifdef HAVE_GETTIMEOFDAY + { + struct timeval time_from_epoch; + + if (gettimeofday(&time_from_epoch, NULL) == 0) { + return (time_from_epoch.tv_sec * QB_TIME_NS_IN_SEC) + + (time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC); + } + } +#endif + + return time(NULL) * QB_TIME_NS_IN_SEC; } uint64_t qb_util_nano_monotonic_hz(void) { #ifdef HAVE_MONOTONIC_CLOCK - uint64_t nano_monotonic_hz; struct timespec ts; - if ((clock_getres(CLOCK_MONOTONIC, &ts) != 0) - && (clock_getres(CLOCK_REALTIME, &ts) != 0)) { - qb_util_perror(LOG_ERR, "couldn't get clock resolution"); + if ((clock_getres(CLOCK_MONOTONIC, &ts) == 0) + || (clock_getres(CLOCK_REALTIME, &ts) == 0)) { + return QB_TIME_NS_IN_SEC / (ts.tv_sec * QB_TIME_NS_IN_SEC + ts.tv_nsec); } +#endif - nano_monotonic_hz = - QB_TIME_NS_IN_SEC / ((ts.tv_sec * QB_TIME_NS_IN_SEC) + ts.tv_nsec); - - return (nano_monotonic_hz); -#else return sysconf(_SC_CLK_TCK); -#endif /* HAVE_MONOTONIC_CLOCK */ } void qb_util_timespec_from_epoch_get(struct timespec *ts) { #ifdef HAVE_MONOTONIC_CLOCK - clock_gettime(QB_CLOCK_REALTIME, ts); -#elif defined(HAVE_GETTIMEOFDAY) - struct timeval time_from_epoch; - gettimeofday(&time_from_epoch, 0); + if (clock_gettime(QB_CLOCK_REALTIME, ts) == 0) { + return; + } +#endif +#ifdef HAVE_GETTIMEOFDAY + { + struct timeval time_from_epoch; + + if (gettimeofday(&time_from_epoch, NULL) == 0) { #ifndef S_SPLINT_S - ts->tv_sec = time_from_epoch.tv_sec; - ts->tv_nsec = time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC; + ts->tv_sec = time_from_epoch.tv_sec; + ts->tv_nsec = time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC; #endif /* S_SPLINT_S */ -#else -#error No high-resolution clock available -#endif /* HAVE_MONOTONIC_CLOCK */ + return; + } + } +#endif + + ts->tv_sec = time(NULL); + ts->tv_nsec = 0; } struct qb_util_stopwatch { uint64_t started; uint64_t stopped; uint32_t split_options; uint32_t split_size; uint32_t split_entries; uint64_t *split_entry_list; }; qb_util_stopwatch_t * qb_util_stopwatch_create(void) { struct qb_util_stopwatch *sw; sw = (struct qb_util_stopwatch *)calloc(1, sizeof(struct qb_util_stopwatch)); return sw; } void qb_util_stopwatch_free(qb_util_stopwatch_t * sw) { free(sw->split_entry_list); free(sw); } void qb_util_stopwatch_start(qb_util_stopwatch_t * sw) { sw->started = qb_util_nano_current_get(); sw->stopped = 0; sw->split_entries = 0; } void qb_util_stopwatch_stop(qb_util_stopwatch_t * sw) { sw->stopped = qb_util_nano_current_get(); } uint64_t qb_util_stopwatch_us_elapsed_get(qb_util_stopwatch_t * sw) { if (sw->stopped == 0 || sw->started == 0) { return 0; } return ((sw->stopped - sw->started) / QB_TIME_NS_IN_USEC); } float qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw) { uint64_t e6; if (sw->stopped == 0 || sw->started == 0) { return 0; } e6 = qb_util_stopwatch_us_elapsed_get(sw); return ((float)e6 / (float)QB_TIME_US_IN_SEC); } int32_t qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw, uint32_t max_splits, uint32_t options) { sw->split_size = max_splits; sw->split_options = options; sw->split_entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * max_splits); if (sw->split_entry_list == NULL) { return -errno; } return 0; } uint64_t qb_util_stopwatch_split(qb_util_stopwatch_t *sw) { uint32_t new_entry_pos; uint64_t time_start; uint64_t time_end; if (sw->split_size == 0) { return 0; } if (!(sw->split_options & QB_UTIL_SW_OVERWRITE) && sw->split_entries == sw->split_size) { return 0; } if (sw->started == 0) { qb_util_stopwatch_start(sw); } new_entry_pos = sw->split_entries % (sw->split_size); sw->split_entry_list[new_entry_pos] = qb_util_nano_current_get(); sw->split_entries++; time_start = sw->split_entry_list[new_entry_pos]; if (sw->split_entries == 1) { /* first entry */ time_end = sw->started; } else if (new_entry_pos == 0) { /* wrap around */ time_end = sw->split_entry_list[sw->split_size - 1]; } else { time_end = sw->split_entry_list[(new_entry_pos - 1) % sw->split_size]; } return (time_start - time_end) / QB_TIME_NS_IN_USEC; } uint32_t qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw) { if (sw->split_entries) { return sw->split_entries - 1; } return sw->split_entries; } uint64_t qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw, uint32_t receint, uint32_t older) { uint64_t time_start; uint64_t time_end; if (sw->started == 0 || receint >= sw->split_entries || older >= sw->split_entries || receint < older) { return 0; } if (sw->split_options & QB_UTIL_SW_OVERWRITE && (receint < (sw->split_entries - sw->split_size) || older < (sw->split_entries - sw->split_size))) { return 0; } time_start = sw->split_entry_list[receint % (sw->split_size)]; if (older == receint) { time_end = sw->started; } else { time_end = sw->split_entry_list[older % (sw->split_size)]; } return (time_start - time_end) / QB_TIME_NS_IN_USEC; } const struct qb_version qb_ver = { .major = QB_VER_MAJOR, .minor = QB_VER_MINOR, .micro = QB_VER_MICRO, .rest = QB_VER_REST, }; const char *const qb_ver_str = QB_VER_STR;