/lib/util.c

https://github.com/beekhof/libqb · C · 369 lines · 308 code · 39 blank · 22 comment · 45 complexity · 831d3c47c9acbddb54f10b0168d383e7 MD5 · raw file

  1. /*
  2. * Copyright (C) 2010 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. #include "util_int.h"
  23. #include <pthread.h>
  24. #include <sys/stat.h>
  25. #include <qb/qbdefs.h>
  26. #include <qb/qbutil.h>
  27. struct qb_thread_lock_s {
  28. qb_thread_lock_type_t type;
  29. #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
  30. pthread_spinlock_t spinlock;
  31. #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
  32. pthread_mutex_t mutex;
  33. };
  34. qb_thread_lock_t *
  35. qb_thread_lock_create(qb_thread_lock_type_t type)
  36. {
  37. struct qb_thread_lock_s *tl = malloc(sizeof(struct qb_thread_lock_s));
  38. int32_t res;
  39. if (tl == NULL) {
  40. return NULL;
  41. }
  42. #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
  43. if (type == QB_THREAD_LOCK_SHORT) {
  44. tl->type = QB_THREAD_LOCK_SHORT;
  45. res = pthread_spin_init(&tl->spinlock, 1);
  46. } else
  47. #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
  48. {
  49. tl->type = QB_THREAD_LOCK_LONG;
  50. res = pthread_mutex_init(&tl->mutex, NULL);
  51. }
  52. if (res == 0) {
  53. return tl;
  54. } else {
  55. free(tl);
  56. return NULL;
  57. }
  58. }
  59. int32_t
  60. qb_thread_lock(qb_thread_lock_t * tl)
  61. {
  62. int32_t res;
  63. #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
  64. if (tl->type == QB_THREAD_LOCK_SHORT) {
  65. res = -pthread_spin_lock(&tl->spinlock);
  66. } else
  67. #endif /* HAVE_PTHREAD_SHARED_SPIN_LOCK */
  68. {
  69. res = -pthread_mutex_lock(&tl->mutex);
  70. }
  71. return res;
  72. }
  73. int32_t
  74. qb_thread_unlock(qb_thread_lock_t * tl)
  75. {
  76. int32_t res;
  77. #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
  78. if (tl->type == QB_THREAD_LOCK_SHORT) {
  79. res = -pthread_spin_unlock(&tl->spinlock);
  80. } else
  81. #endif
  82. {
  83. res = -pthread_mutex_unlock(&tl->mutex);
  84. }
  85. return res;
  86. }
  87. int32_t
  88. qb_thread_trylock(qb_thread_lock_t * tl)
  89. {
  90. int32_t res;
  91. #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
  92. if (tl->type == QB_THREAD_LOCK_SHORT) {
  93. res = -pthread_spin_trylock(&tl->spinlock);
  94. } else
  95. #endif
  96. {
  97. res = -pthread_mutex_trylock(&tl->mutex);
  98. }
  99. return res;
  100. }
  101. int32_t
  102. qb_thread_lock_destroy(qb_thread_lock_t * tl)
  103. {
  104. int32_t res;
  105. #ifdef HAVE_PTHREAD_SHARED_SPIN_LOCK
  106. if (tl->type == QB_THREAD_LOCK_SHORT) {
  107. res = -pthread_spin_destroy(&tl->spinlock);
  108. } else
  109. #endif
  110. {
  111. res = -pthread_mutex_destroy(&tl->mutex);
  112. }
  113. free(tl);
  114. return res;
  115. }
  116. void
  117. qb_timespec_add_ms(struct timespec *ts, int32_t ms)
  118. {
  119. #ifndef S_SPLINT_S
  120. ts->tv_sec += ms / 1000;
  121. ts->tv_nsec += (ms % 1000) * QB_TIME_NS_IN_MSEC;
  122. if (ts->tv_nsec >= 1000000000L) {
  123. ts->tv_sec++;
  124. ts->tv_nsec = ts->tv_nsec - 1000000000L;
  125. }
  126. #endif /* S_SPLINT_S */
  127. }
  128. #ifdef HAVE_MONOTONIC_CLOCK
  129. uint64_t
  130. qb_util_nano_current_get(void)
  131. {
  132. uint64_t nano_monotonic;
  133. struct timespec ts;
  134. clock_gettime(CLOCK_MONOTONIC, &ts);
  135. nano_monotonic =
  136. (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec;
  137. return (nano_monotonic);
  138. }
  139. uint64_t
  140. qb_util_nano_from_epoch_get(void)
  141. {
  142. uint64_t nano_monotonic;
  143. struct timespec ts;
  144. #ifdef CLOCK_REALTIME_COARSE
  145. clock_gettime(CLOCK_REALTIME_COARSE, &ts);
  146. #else
  147. clock_gettime(CLOCK_REALTIME, &ts);
  148. #endif
  149. nano_monotonic =
  150. (ts.tv_sec * QB_TIME_NS_IN_SEC) + (uint64_t) ts.tv_nsec;
  151. return (nano_monotonic);
  152. }
  153. uint64_t
  154. qb_util_nano_monotonic_hz(void)
  155. {
  156. uint64_t nano_monotonic_hz;
  157. struct timespec ts;
  158. clock_getres(CLOCK_MONOTONIC, &ts);
  159. nano_monotonic_hz =
  160. QB_TIME_NS_IN_SEC / ((ts.tv_sec * QB_TIME_NS_IN_SEC) + ts.tv_nsec);
  161. return (nano_monotonic_hz);
  162. }
  163. void
  164. qb_util_timespec_from_epoch_get(struct timespec *ts)
  165. {
  166. #ifdef CLOCK_REALTIME_COARSE
  167. clock_gettime(CLOCK_REALTIME_COARSE, ts);
  168. #else
  169. clock_gettime(CLOCK_REALTIME, ts);
  170. #endif
  171. }
  172. #else
  173. uint64_t
  174. qb_util_nano_current_get(void)
  175. {
  176. return qb_util_nano_from_epoch_get();
  177. }
  178. uint64_t
  179. qb_util_nano_monotonic_hz(void)
  180. {
  181. return sysconf(_SC_CLK_TCK);
  182. }
  183. void
  184. qb_util_timespec_from_epoch_get(struct timespec *ts)
  185. {
  186. struct timeval time_from_epoch;
  187. gettimeofday(&time_from_epoch, 0);
  188. #ifndef S_SPLINT_S
  189. ts->tv_sec = time_from_epoch.tv_sec;
  190. ts->tv_nsec = time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC;
  191. #endif /* S_SPLINT_S */
  192. }
  193. uint64_t
  194. qb_util_nano_from_epoch_get(void)
  195. {
  196. uint64_t nano_from_epoch;
  197. struct timeval time_from_epoch;
  198. gettimeofday(&time_from_epoch, 0);
  199. nano_from_epoch = ((time_from_epoch.tv_sec * QB_TIME_NS_IN_SEC) +
  200. (time_from_epoch.tv_usec * QB_TIME_NS_IN_USEC));
  201. return (nano_from_epoch);
  202. }
  203. #endif /* HAVE_MONOTONIC_CLOCK */
  204. struct qb_util_stopwatch {
  205. uint64_t started;
  206. uint64_t stopped;
  207. uint32_t split_options;
  208. uint32_t split_size;
  209. uint32_t split_entries;
  210. uint64_t *split_entry_list;
  211. };
  212. qb_util_stopwatch_t *
  213. qb_util_stopwatch_create(void)
  214. {
  215. struct qb_util_stopwatch *sw;
  216. sw = (struct qb_util_stopwatch *)calloc(1, sizeof(struct qb_util_stopwatch));
  217. return sw;
  218. }
  219. void
  220. qb_util_stopwatch_free(qb_util_stopwatch_t * sw)
  221. {
  222. free(sw->split_entry_list);
  223. free(sw);
  224. }
  225. void
  226. qb_util_stopwatch_start(qb_util_stopwatch_t * sw)
  227. {
  228. sw->started = qb_util_nano_current_get();
  229. sw->stopped = 0;
  230. sw->split_entries = 0;
  231. }
  232. void
  233. qb_util_stopwatch_stop(qb_util_stopwatch_t * sw)
  234. {
  235. sw->stopped = qb_util_nano_current_get();
  236. }
  237. uint64_t
  238. qb_util_stopwatch_us_elapsed_get(qb_util_stopwatch_t * sw)
  239. {
  240. if (sw->stopped == 0 || sw->started == 0) {
  241. return 0;
  242. }
  243. return ((sw->stopped - sw->started) / QB_TIME_NS_IN_USEC);
  244. }
  245. float
  246. qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw)
  247. {
  248. uint64_t e6;
  249. if (sw->stopped == 0 || sw->started == 0) {
  250. return 0;
  251. }
  252. e6 = qb_util_stopwatch_us_elapsed_get(sw);
  253. return ((float)e6 / (float)QB_TIME_US_IN_SEC);
  254. }
  255. int32_t
  256. qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw,
  257. uint32_t max_splits, uint32_t options)
  258. {
  259. sw->split_size = max_splits;
  260. sw->split_options = options;
  261. sw->split_entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * max_splits);
  262. if (sw->split_entry_list == NULL) {
  263. return -errno;
  264. }
  265. return 0;
  266. }
  267. uint64_t
  268. qb_util_stopwatch_split(qb_util_stopwatch_t *sw)
  269. {
  270. uint32_t new_entry_pos;
  271. uint64_t time_start;
  272. uint64_t time_end;
  273. if (sw->split_size == 0) {
  274. return 0;
  275. }
  276. if (!(sw->split_options & QB_UTIL_SW_OVERWRITE) &&
  277. sw->split_entries == sw->split_size) {
  278. return 0;
  279. }
  280. if (sw->started == 0) {
  281. qb_util_stopwatch_start(sw);
  282. }
  283. new_entry_pos = sw->split_entries % (sw->split_size);
  284. sw->split_entry_list[new_entry_pos] = qb_util_nano_current_get();
  285. sw->split_entries++;
  286. time_start = sw->split_entry_list[new_entry_pos];
  287. if (sw->split_entries == 1) {
  288. /* first entry */
  289. time_end = sw->started;
  290. } else if (new_entry_pos == 0) {
  291. /* wrap around */
  292. time_end = sw->split_entry_list[sw->split_size - 1];
  293. } else {
  294. time_end = sw->split_entry_list[(new_entry_pos - 1) % sw->split_size];
  295. }
  296. return (time_start - time_end) / QB_TIME_NS_IN_USEC;
  297. }
  298. uint32_t
  299. qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw)
  300. {
  301. if (sw->split_entries) {
  302. return sw->split_entries - 1;
  303. }
  304. return sw->split_entries;
  305. }
  306. uint64_t
  307. qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw,
  308. uint32_t receint, uint32_t older)
  309. {
  310. uint64_t time_start;
  311. uint64_t time_end;
  312. if (sw->started == 0 ||
  313. receint >= sw->split_entries ||
  314. older >= sw->split_entries ||
  315. receint < older) {
  316. return 0;
  317. }
  318. if (sw->split_options & QB_UTIL_SW_OVERWRITE &&
  319. (receint < (sw->split_entries - sw->split_size) ||
  320. older < (sw->split_entries - sw->split_size))) {
  321. return 0;
  322. }
  323. time_start = sw->split_entry_list[receint % (sw->split_size)];
  324. if (older == receint) {
  325. time_end = sw->started;
  326. } else {
  327. time_end = sw->split_entry_list[older % (sw->split_size)];
  328. }
  329. return (time_start - time_end) / QB_TIME_NS_IN_USEC;
  330. }