diff --git a/include/tlist.h b/include/tlist.h index 6735c98..fe3d9a6 100644 --- a/include/tlist.h +++ b/include/tlist.h @@ -1,488 +1,490 @@ /* * Copyright (c) 2006-2007, 2009-2021 Red Hat, Inc. * * Author: Jan Friesse * Steven Dake * * This file is part of libqb. * * 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 . */ #ifndef QB_TLIST_H_DEFINED #define QB_TLIST_H_DEFINED #include "os_base.h" #include #include #include #ifndef TIMER_HANDLE typedef void *timer_handle; #define TIMER_HANDLE #endif static int64_t timerlist_hertz; struct timerlist { struct timerlist_timer **heap_entries; size_t allocated; size_t size; pthread_mutex_t list_mutex; }; struct timerlist_timer { uint64_t expire_time; int32_t is_absolute_timer; void (*timer_fn) (void *data); void *data; timer_handle handle_addr; size_t heap_pos; }; /* * Heap helper functions */ static inline size_t timerlist_heap_index_left(size_t index) { return (2 * index + 1); } static inline size_t timerlist_heap_index_right(size_t index) { return (2 * index + 2); } static inline size_t timerlist_heap_index_parent(size_t index) { return ((index - 1) / 2); } static inline void timerlist_heap_entry_set(struct timerlist *timerlist, size_t item_pos, struct timerlist_timer *timer) { assert(item_pos < timerlist->size); timerlist->heap_entries[item_pos] = timer; timerlist->heap_entries[item_pos]->heap_pos = item_pos; } static inline struct timerlist_timer * timerlist_heap_entry_get(struct timerlist *timerlist, size_t item_pos) { assert(item_pos < timerlist->size); return (timerlist->heap_entries[item_pos]); } static inline int timerlist_entry_cmp(const struct timerlist_timer *t1, const struct timerlist_timer *t2) { if (t1->expire_time == t2->expire_time) { return (0); } else if (t1->expire_time < t2->expire_time) { return (-1); } else { return (1); } } static inline void timerlist_heap_sift_up(struct timerlist *timerlist, size_t item_pos) { size_t parent_pos; struct timerlist_timer *parent_timer; struct timerlist_timer *timer; timer = timerlist_heap_entry_get(timerlist, item_pos); parent_pos = timerlist_heap_index_parent(item_pos); while (item_pos > 0 && (parent_timer = timerlist_heap_entry_get(timerlist, parent_pos), timerlist_entry_cmp(parent_timer, timer) > 0)) { /* * Swap item and parent */ timerlist_heap_entry_set(timerlist, parent_pos, timer); timerlist_heap_entry_set(timerlist, item_pos, parent_timer); item_pos = parent_pos; parent_pos = timerlist_heap_index_parent(item_pos); } } static inline void timerlist_heap_sift_down(struct timerlist *timerlist, size_t item_pos) { int cont; size_t left_pos, right_pos, smallest_pos; struct timerlist_timer *left_entry; struct timerlist_timer *right_entry; struct timerlist_timer *smallest_entry; struct timerlist_timer *tmp_entry; cont = 1; while (cont) { smallest_pos = item_pos; left_pos = timerlist_heap_index_left(item_pos); right_pos = timerlist_heap_index_right(item_pos); smallest_entry = timerlist_heap_entry_get(timerlist, smallest_pos); if (left_pos < timerlist->size && (left_entry = timerlist_heap_entry_get(timerlist, left_pos), timerlist_entry_cmp(left_entry, smallest_entry) < 0)) { smallest_entry = left_entry; smallest_pos = left_pos; } if (right_pos < timerlist->size && (right_entry = timerlist_heap_entry_get(timerlist, right_pos), timerlist_entry_cmp(right_entry, smallest_entry) < 0)) { smallest_entry = right_entry; smallest_pos = right_pos; } if (smallest_pos == item_pos) { /* * Item is smallest (or has no children) -> heap property is restored */ cont = 0; } else { /* * Swap item with smallest child */ tmp_entry = timerlist_heap_entry_get(timerlist, item_pos); timerlist_heap_entry_set(timerlist, item_pos, smallest_entry); timerlist_heap_entry_set(timerlist, smallest_pos, tmp_entry); item_pos = smallest_pos; } } } static inline void timerlist_heap_delete(struct timerlist *timerlist, struct timerlist_timer *entry) { size_t entry_pos; struct timerlist_timer *replacement_entry; int cmp_entries; entry_pos = entry->heap_pos; entry->heap_pos = (~(size_t)0); /* * Swap element with last element */ replacement_entry = timerlist_heap_entry_get(timerlist, timerlist->size - 1); timerlist_heap_entry_set(timerlist, entry_pos, replacement_entry); /* * And "remove" last element (= entry) */ timerlist->size--; /* * Up (or down) heapify based on replacement item size */ cmp_entries = timerlist_entry_cmp(replacement_entry, entry); if (cmp_entries < 0) { timerlist_heap_sift_up(timerlist, entry_pos); } else if (cmp_entries > 0) { timerlist_heap_sift_down(timerlist, entry_pos); } } /* * Check if heap is valid. * - Shape property is always fullfiled because of storage in array * - Check heap property */ static inline int timerlist_debug_is_valid_heap(struct timerlist *timerlist) { size_t i; size_t left_pos, right_pos; struct timerlist_timer *left_entry; struct timerlist_timer *right_entry; struct timerlist_timer *cur_entry; for (i = 0; i < timerlist->size; i++) { cur_entry = timerlist_heap_entry_get(timerlist, i); left_pos = timerlist_heap_index_left(i); right_pos = timerlist_heap_index_right(i); if (left_pos < timerlist->size && (left_entry = timerlist_heap_entry_get(timerlist, left_pos), timerlist_entry_cmp(left_entry, cur_entry) < 0)) { return (0); } if (right_pos < timerlist->size && (right_entry = timerlist_heap_entry_get(timerlist, right_pos), timerlist_entry_cmp(right_entry, cur_entry) < 0)) { return (0); } } return (1); } /* * Main functions implementation */ static inline void timerlist_init(struct timerlist *timerlist) { memset(timerlist, 0, sizeof(*timerlist)); timerlist->heap_entries = NULL; pthread_mutex_init(&timerlist->list_mutex, NULL); timerlist_hertz = qb_util_nano_monotonic_hz(); } static inline void timerlist_destroy(struct timerlist *timerlist) { size_t zi; pthread_mutex_destroy(&timerlist->list_mutex); for (zi = 0; zi < timerlist->size; zi++) { free(timerlist->heap_entries[zi]); } free(timerlist->heap_entries); } static inline int32_t timerlist_add(struct timerlist *timerlist, struct timerlist_timer *timer) { size_t new_size; struct timerlist_timer **new_heap_entries; int32_t res = 0; - if (pthread_mutex_lock(&timerlist->list_mutex)) { - return -errno; + if ( (res=pthread_mutex_lock(&timerlist->list_mutex))) { + return -res; } /* * Check that heap array is large enough */ if (timerlist->size + 1 > timerlist->allocated) { new_size = (timerlist->allocated + 1) * 2; new_heap_entries = realloc(timerlist->heap_entries, new_size * sizeof(timerlist->heap_entries[0])); if (new_heap_entries == NULL) { res = -errno; goto cleanup; } timerlist->allocated = new_size; timerlist->heap_entries = new_heap_entries; } timerlist->size++; timerlist_heap_entry_set(timerlist, timerlist->size - 1, timer); timerlist_heap_sift_up(timerlist, timerlist->size - 1); cleanup: pthread_mutex_unlock(&timerlist->list_mutex); return res; } static inline int32_t timerlist_add_duration(struct timerlist *timerlist, void (*timer_fn) (void *data), void *data, uint64_t nano_duration, timer_handle * handle) { int res; struct timerlist_timer *timer; timer = (struct timerlist_timer *)malloc(sizeof(struct timerlist_timer)); if (timer == NULL) { return -ENOMEM; } timer->expire_time = qb_util_nano_current_get() + nano_duration; timer->is_absolute_timer = QB_FALSE; timer->data = data; timer->timer_fn = timer_fn; timer->handle_addr = handle; res = timerlist_add(timerlist, timer); if (res) { free(timer); return res; } *handle = timer; return (0); } static inline int32_t timerlist_del(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; + int res; - if (pthread_mutex_lock(&timerlist->list_mutex)) { - return -errno; + if ( (res=pthread_mutex_lock(&timerlist->list_mutex))) { + return -res; } memset(timer->handle_addr, 0, sizeof(struct timerlist_timer *)); timerlist_heap_delete(timerlist, timer); free(timer); pthread_mutex_unlock(&timerlist->list_mutex); return 0; } static inline uint64_t timerlist_expire_time(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; return (timer->expire_time); } static inline void timerlist_pre_dispatch(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; memset(timer->handle_addr, 0, sizeof(struct timerlist_timer *)); timerlist_heap_delete(timerlist, timer); } static inline void timerlist_post_dispatch(struct timerlist *timerlist, timer_handle _timer_handle) { struct timerlist_timer *timer = (struct timerlist_timer *)_timer_handle; free(timer); } /* * returns the number of msec until the next timer will expire for use with poll */ static inline uint64_t timerlist_msec_duration_to_expire(struct timerlist *timerlist) { struct timerlist_timer *timer_from_list; volatile uint64_t current_time; volatile uint64_t msec_duration_to_expire; /* * There is really no reasonable value to return when mutex lock fails */ if (pthread_mutex_lock(&timerlist->list_mutex)) { return (-1); } /* * empty list, no expire */ if (timerlist->size == 0) { pthread_mutex_unlock(&timerlist->list_mutex); return (-1); } timer_from_list = timerlist_heap_entry_get(timerlist, 0); /* * Mutex is no longer needed */ pthread_mutex_unlock(&timerlist->list_mutex); if (timer_from_list->is_absolute_timer) { current_time = qb_util_nano_from_epoch_get(); } else { current_time = qb_util_nano_current_get(); } /* * timer at head of list is expired, zero msecs required */ if (timer_from_list->expire_time < current_time) { return (0); } msec_duration_to_expire = ((timer_from_list->expire_time - current_time) / QB_TIME_NS_IN_MSEC) + (1000 / timerlist_hertz); return (msec_duration_to_expire); } /* * Expires any timers that should be expired */ static inline int32_t timerlist_expire(struct timerlist *timerlist) { struct timerlist_timer *timer; uint64_t current_time_from_epoch; uint64_t current_monotonic_time; uint64_t current_time; + int res; current_monotonic_time = qb_util_nano_current_get(); current_time_from_epoch = qb_util_nano_from_epoch_get(); - if (pthread_mutex_lock(&timerlist->list_mutex)) { - return -errno; + if ( (res=pthread_mutex_lock(&timerlist->list_mutex))) { + return -res; } while (timerlist->size > 0) { timer = timerlist_heap_entry_get(timerlist, 0); current_time = (timer-> is_absolute_timer ? current_time_from_epoch : current_monotonic_time); if (timer->expire_time < current_time) { timerlist_pre_dispatch(timerlist, timer); timer->timer_fn(timer->data); timerlist_post_dispatch(timerlist, timer); } else { break; /* for timer iteration */ } } pthread_mutex_unlock(&timerlist->list_mutex); return (0); } #endif /* QB_TLIST_H_DEFINED */ diff --git a/lib/loop_timerlist.c b/lib/loop_timerlist.c index 59cb1a1..9aef348 100644 --- a/lib/loop_timerlist.c +++ b/lib/loop_timerlist.c @@ -1,357 +1,358 @@ /* * Copyright (C) 2010 Red Hat, Inc. * * Author: Angus Salkeld * * This file is part of libqb. * * 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 #include #include #include #include #include "loop_int.h" #include "util_int.h" #include "tlist.h" struct qb_loop_timer { struct qb_loop_item item; qb_loop_timer_dispatch_fn dispatch_fn; enum qb_loop_priority p; timer_handle timerlist_handle; enum qb_poll_entry_state state; int32_t check; uint32_t install_pos; }; struct qb_timer_source { struct qb_loop_source s; struct timerlist timerlist; qb_array_t *timers; size_t timer_entry_count; pthread_mutex_t lock; }; static void timer_dispatch(struct qb_loop_item *item, enum qb_loop_priority p) { struct qb_loop_timer *timer = (struct qb_loop_timer *)item; assert(timer->state == QB_POLL_ENTRY_JOBLIST); timer->check = 0; timer->dispatch_fn(timer->item.user_data); timer->state = QB_POLL_ENTRY_EMPTY; } static int32_t expired_timers; static void make_job_from_tmo(void *data) { struct qb_loop_timer *t = (struct qb_loop_timer *)data; struct qb_loop *l = t->item.source->l; assert(t->state == QB_POLL_ENTRY_ACTIVE); qb_loop_level_item_add(&l->level[t->p], &t->item); t->state = QB_POLL_ENTRY_JOBLIST; expired_timers++; } static int32_t expire_the_timers(struct qb_loop_source *s, int32_t ms_timeout) { struct qb_timer_source *ts = (struct qb_timer_source *)s; expired_timers = 0; if (timerlist_expire(&ts->timerlist) != 0) { qb_util_log(LOG_ERR, "timerlist_expire failed"); } return expired_timers; } int32_t qb_loop_timer_msec_duration_to_expire(struct qb_loop_source * timer_source) { struct qb_timer_source *my_src = (struct qb_timer_source *)timer_source; uint64_t left = timerlist_msec_duration_to_expire(&my_src->timerlist); if (left != -1 && left > 0xFFFFFFFF) { left = 0xFFFFFFFE; } return left; } struct qb_loop_source * qb_loop_timer_create(struct qb_loop *l) { struct qb_timer_source *my_src = malloc(sizeof(struct qb_timer_source)); if (my_src == NULL) { return NULL; } my_src->s.l = l; my_src->s.dispatch_and_take_back = timer_dispatch; my_src->s.poll = expire_the_timers; timerlist_init(&my_src->timerlist); my_src->timers = qb_array_create_2(16, sizeof(struct qb_loop_timer), 16); my_src->timer_entry_count = 0; pthread_mutex_init(&my_src->lock, NULL); return (struct qb_loop_source *)my_src; } void qb_loop_timer_destroy(struct qb_loop *l) { struct qb_timer_source *my_src = (struct qb_timer_source *)l->timer_source; timerlist_destroy(&my_src->timerlist); qb_array_free(my_src->timers); free(l->timer_source); } static int32_t _timer_from_handle_(struct qb_timer_source *s, qb_loop_timer_handle handle_in, struct qb_loop_timer **timer_pt) { int32_t rc; int32_t check; uint32_t install_pos; struct qb_loop_timer *timer; if (handle_in == 0) { return -EINVAL; } check = handle_in >> 32; install_pos = handle_in & UINT32_MAX; rc = qb_array_index(s->timers, install_pos, (void **)&timer); if (rc != 0) { return rc; } if (timer->check != check) { return -EINVAL; } *timer_pt = timer; return 0; } static int32_t _get_empty_array_position_(struct qb_timer_source *s) { int32_t install_pos; int32_t res = 0; struct qb_loop_timer *timer; for (install_pos = 0; install_pos < s->timer_entry_count; install_pos++) { assert(qb_array_index(s->timers, install_pos, (void **)&timer) == 0); if (timer->state == QB_POLL_ENTRY_EMPTY) { return install_pos; } } res = qb_array_grow(s->timers, s->timer_entry_count + 1); if (res != 0) { return res; } s->timer_entry_count++; install_pos = s->timer_entry_count - 1; return install_pos; } int32_t qb_loop_timer_add(struct qb_loop * lp, enum qb_loop_priority p, uint64_t nsec_duration, void *data, qb_loop_timer_dispatch_fn timer_fn, qb_loop_timer_handle * timer_handle_out) { struct qb_loop_timer *t; struct qb_timer_source *my_src; int32_t i; + int res; struct qb_loop *l = lp; if (l == NULL) { l = qb_loop_default_get(); } if (l == NULL || timer_fn == NULL) { return -EINVAL; } my_src = (struct qb_timer_source *)l->timer_source; - if (pthread_mutex_lock(&my_src->lock)) { - return -errno; + if ( (res=pthread_mutex_lock(&my_src->lock))) { + return -res; } i = _get_empty_array_position_(my_src); assert(qb_array_index(my_src->timers, i, (void **)&t) >= 0); t->state = QB_POLL_ENTRY_ACTIVE; t->install_pos = i; t->item.user_data = data; t->item.source = (struct qb_loop_source *)my_src; t->dispatch_fn = timer_fn; t->p = p; qb_list_init(&t->item.list); /* Unlock here to stop anyone else changing the state while we're initializing */ pthread_mutex_unlock(&my_src->lock); /* * Make sure just positive integers are used for the integrity(?) * checks within 2^32 address space, if we miss 200 times in a row * (just 0 is concerned per specification of random), the PRNG may be * broken -> the value is unspecified, subject of previous assignment. */ for (i = 0; i < 200; i++) { t->check = random(); if (t->check > 0) { break; /* covers also t->check == UINT32_MAX */ } } if (timer_handle_out) { *timer_handle_out = (((uint64_t) (t->check)) << 32) | t->install_pos; } return timerlist_add_duration(&my_src->timerlist, make_job_from_tmo, t, nsec_duration, &t->timerlist_handle); } int32_t qb_loop_timer_del(struct qb_loop * lp, qb_loop_timer_handle th) { struct qb_timer_source *s; struct qb_loop_timer *t; int32_t res; struct qb_loop *l = lp; if (l == NULL) { l = qb_loop_default_get(); } s = (struct qb_timer_source *)l->timer_source; res = _timer_from_handle_(s, th, &t); if (res != 0) { return res; } if (t->state == QB_POLL_ENTRY_DELETED) { qb_util_log(LOG_WARNING, "timer already deleted"); return 0; } if (t->state != QB_POLL_ENTRY_ACTIVE && t->state != QB_POLL_ENTRY_JOBLIST) { return -EINVAL; } if (t->state == QB_POLL_ENTRY_JOBLIST) { qb_loop_level_item_del(&l->level[t->p], &t->item); } if (t->timerlist_handle) { if (timerlist_del(&s->timerlist, t->timerlist_handle) != 0) { qb_util_log(LOG_ERR, "Could not delete timer from timerlist"); } } t->state = QB_POLL_ENTRY_EMPTY; return 0; } uint64_t qb_loop_timer_expire_time_get(struct qb_loop * lp, qb_loop_timer_handle th) { struct qb_timer_source *s; struct qb_loop_timer *t; int32_t res; struct qb_loop *l = lp; if (l == NULL) { l = qb_loop_default_get(); } s = (struct qb_timer_source *)l->timer_source; res = _timer_from_handle_(s, th, &t); if (res != 0) { return 0; } if (t->state != QB_POLL_ENTRY_ACTIVE) { return 0; } return timerlist_expire_time(&s->timerlist, t->timerlist_handle); } uint64_t qb_loop_timer_expire_time_remaining(struct qb_loop * lp, qb_loop_timer_handle th) { uint64_t current_ns; /* NOTE: while it does not appear that absolute timers are used anywhere, * we may as well respect this pattern in case that changes. * Unfortunately, that means we do need to repeat timer fetch code from qb_loop_timer_expire_time_get * rather than just a simple call to qb_loop_timer_expire_time_get and qb_util_nano_current_get. */ struct qb_timer_source *s; struct qb_loop_timer *t; int32_t res; struct qb_loop *l = lp; if (l == NULL) { l = qb_loop_default_get(); } s = (struct qb_timer_source *)l->timer_source; res = _timer_from_handle_(s, th, &t); if (res != 0) { return 0; } struct timerlist_timer *timer = (struct timerlist_timer *)t->timerlist_handle; if (timer->is_absolute_timer) { current_ns = qb_util_nano_from_epoch_get(); } else { current_ns = qb_util_nano_current_get(); } uint64_t timer_ns = timerlist_expire_time(&s->timerlist, t->timerlist_handle); /* since time estimation is racy by nature, I'll try to check the state late, * and try to understand that no matter what, the timer might have expired in the mean time */ if (t->state != QB_POLL_ENTRY_ACTIVE) { return 0; } if (timer_ns < current_ns) { return 0; // respect the "expired" contract } return timer_ns - current_ns; } int32_t qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th) { return (qb_loop_timer_expire_time_get(l, th) > 0); } diff --git a/lib/rpl_sem.c b/lib/rpl_sem.c index 7daeb4f..cb73f9d 100644 --- a/lib/rpl_sem.c +++ b/lib/rpl_sem.c @@ -1,214 +1,214 @@ /* * Copyright (C) 2007 Pingtel Corp., certain elements licensed under a Contributor Agreement. * Contributors retain copyright to elements licensed under a Contributor Agreement. * Licensed to the User under the LGPL license. * * Modified by: Angus Salkeld * Copyright (C) 2012 Red Hat, Inc. * To conform to posix API and support process shared semaphores. * * The bsd posix semaphore implementation does not have support for timing * out while waiting for a synchronization object. This uses the * pthread_cond_timedwait function and a mutex to build all the other * synchronization objecs with timeout capabilities. */ #include "os_base.h" #include #include "rpl_sem.h" #include #include int rpl_sem_init(rpl_sem_t * sem, int pshared, unsigned int count) { int rc; pthread_mutexattr_t mattr; pthread_condattr_t cattr; #ifndef HAVE_RPL_PSHARED_SEMAPHORE if (pshared) { errno = ENOSYS; return -1; } #endif /* HAVE_RPL_PSHARED_SEMAPHORE */ sem->count = count; sem->destroy_request = QB_FALSE; (void)pthread_mutexattr_init(&mattr); (void)pthread_condattr_init(&cattr); #ifdef HAVE_RPL_PSHARED_SEMAPHORE if (pshared) { rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); if (rc != 0) { goto cleanup; } rc = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); if (rc != 0) { goto cleanup; } } #endif /* HAVE_RPL_PSHARED_SEMAPHORE */ rc = pthread_mutex_init(&sem->mutex, &mattr); if (rc != 0) { goto cleanup; } rc = pthread_cond_init(&sem->cond, &cattr); if (rc != 0) { goto cleanup_mutex; } return 0; cleanup_mutex: pthread_mutex_destroy(&sem->mutex); cleanup: pthread_mutexattr_destroy(&mattr); pthread_condattr_destroy(&cattr); return rc; } static int _rpl_sem_timedwait(rpl_sem_t * sem, const struct timespec *timeout) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { - return -errno; + return -retval; } if (sem->destroy_request) { retval = -EINVAL; goto unlock_it; } /* wait for sem->count to be not zero, or error */ while (0 == retval && !sem->count) { retval = -pthread_cond_timedwait(&sem->cond, &sem->mutex, timeout); } if (sem->destroy_request) { retval = -EINVAL; goto unlock_it; } switch (retval) { case 0: /* retval is 0 and sem->count is not, the sem is ours */ sem->count--; break; case ETIMEDOUT: /* timedout waiting for count to be not zero */ retval = -EAGAIN; break; default: break; } unlock_it: pthread_mutex_unlock(&sem->mutex); return retval; } int rpl_sem_wait(rpl_sem_t * sem) { struct timespec ts_timeout; int32_t rc; do { qb_util_timespec_from_epoch_get(&ts_timeout); qb_timespec_add_ms(&ts_timeout, 1000); rc = _rpl_sem_timedwait(sem, &ts_timeout); } while (rc == -EAGAIN); if (rc < 0) { errno = -rc; return -1; } return 0; } int rpl_sem_timedwait(rpl_sem_t * sem, const struct timespec *timeout) { int rc = _rpl_sem_timedwait(sem, timeout); if (rc < 0) { errno = -rc; return -1; } return 0; } int rpl_sem_trywait(rpl_sem_t * sem) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } if (sem->count) { sem->count--; pthread_mutex_unlock(&sem->mutex); return 0; } errno = EAGAIN; pthread_mutex_unlock(&sem->mutex); return -1; } int rpl_sem_post(rpl_sem_t * sem) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } sem->count++; retval = pthread_cond_broadcast(&sem->cond); pthread_mutex_unlock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } return 0; } int rpl_sem_getvalue(rpl_sem_t * sem, int *sval) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } *sval = sem->count; pthread_mutex_unlock(&sem->mutex); return 0; } int rpl_sem_destroy(rpl_sem_t * sem) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } sem->destroy_request = QB_TRUE; pthread_mutex_unlock(&sem->mutex); (void)pthread_cond_broadcast(&sem->cond); (void)pthread_cond_destroy(&sem->cond); (void)pthread_mutex_destroy(&sem->mutex); return 0; }