Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

timers: Add some locking #436

Merged
merged 1 commit into from
Feb 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions include/tlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ static int64_t timerlist_hertz;

struct timerlist {
struct qb_list_head timer_head;
pthread_mutex_t list_mutex;
};

struct timerlist_timer {
Expand All @@ -50,16 +51,20 @@ struct timerlist_timer {
static inline void timerlist_init(struct timerlist *timerlist)
{
qb_list_init(&timerlist->timer_head);
pthread_mutex_init(&timerlist->list_mutex, NULL);
timerlist_hertz = qb_util_nano_monotonic_hz();
}

static inline void timerlist_add(struct timerlist *timerlist,
static inline int32_t timerlist_add(struct timerlist *timerlist,
struct timerlist_timer *timer)
{
struct qb_list_head *timer_list = 0;
struct timerlist_timer *timer_from_list;
int32_t found = QB_FALSE;

if (pthread_mutex_lock(&timerlist->list_mutex)) {
return -errno;
}
qb_list_for_each(timer_list, &timerlist->timer_head) {

timer_from_list = qb_list_entry(timer_list,
Expand All @@ -74,6 +79,8 @@ static inline void timerlist_add(struct timerlist *timerlist,
if (found == QB_FALSE) {
qb_list_add_tail(&timer->list, &timerlist->timer_head);
}
pthread_mutex_unlock(&timerlist->list_mutex);
return 0;
}

static inline int32_t timerlist_add_duration(struct timerlist *timerlist,
Expand All @@ -82,6 +89,7 @@ static inline int32_t timerlist_add_duration(struct timerlist *timerlist,
uint64_t nano_duration,
timer_handle * handle)
{
int res;
struct timerlist_timer *timer;

timer =
Expand All @@ -95,7 +103,11 @@ static inline int32_t timerlist_add_duration(struct timerlist *timerlist,
timer->data = data;
timer->timer_fn = timer_fn;
timer->handle_addr = handle;
timerlist_add(timerlist, timer);
res = timerlist_add(timerlist, timer);
if (res) {
free(timer);
return res;
}

*handle = timer;
return (0);
Expand Down
8 changes: 8 additions & 0 deletions lib/loop_timerlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct qb_timer_source {
struct timerlist timerlist;
qb_array_t *timers;
size_t timer_entry_count;
pthread_mutex_t lock;
};

static void
Expand Down Expand Up @@ -104,6 +105,7 @@ qb_loop_timer_create(struct qb_loop *l)
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;
}
Expand Down Expand Up @@ -192,6 +194,9 @@ qb_loop_timer_add(struct qb_loop * lp,
}
my_src = (struct qb_timer_source *)l->timer_source;

if (pthread_mutex_lock(&my_src->lock)) {
return -errno;
}
i = _get_empty_array_position_(my_src);
assert(qb_array_index(my_src->timers, i, (void **)&t) >= 0);
t->state = QB_POLL_ENTRY_ACTIVE;
Expand All @@ -202,6 +207,9 @@ qb_loop_timer_add(struct qb_loop * lp,
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
Expand Down
51 changes: 51 additions & 0 deletions tests/check_loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,56 @@ START_TEST(test_loop_timer_basic)
}
END_TEST

static void *loop_timer_thread(void *arg)
{
int res;
qb_loop_t *l = (qb_loop_t *)arg;
qb_loop_timer_handle test_tht;

res = qb_loop_timer_add(l, QB_LOOP_LOW, 5*QB_TIME_NS_IN_MSEC, l, one_shot_tmo, &test_tht);
ck_assert_int_eq(res, 0);

res = qb_loop_timer_is_running(l, test_th);
ck_assert_int_eq(res, QB_TRUE);

sleep(5);

return (void *)0;
}

/* This test will probably never fail (unless something
really bad happens) but is useful for running under
helgrind to find threading issues */
START_TEST(test_loop_timer_threads)
{
int32_t res;
pthread_t thr;
qb_loop_t *l = qb_loop_create();
ck_assert(l != NULL);

res = pthread_create(&thr, NULL, loop_timer_thread, l);

res = qb_loop_timer_add(l, QB_LOOP_LOW, 7*QB_TIME_NS_IN_MSEC, l, reset_one_shot_tmo, &reset_th);
ck_assert_int_eq(res, 0);

res = qb_loop_timer_add(l, QB_LOOP_HIGH, 20*QB_TIME_NS_IN_MSEC, l, check_time_left, &test_th2);
ck_assert_int_eq(res, 0);

res = qb_loop_timer_add(l, QB_LOOP_LOW, 60*QB_TIME_NS_IN_MSEC, l, job_stop, &test_th);
ck_assert_int_eq(res, 0);

qb_loop_run(l);

ck_assert_int_eq(reset_timer_step, 2);

pthread_join(thr, NULL);
qb_loop_destroy(l);
}
END_TEST




struct qb_stop_watch {
uint64_t start;
uint64_t end;
Expand Down Expand Up @@ -742,6 +792,7 @@ loop_timer_suite(void)
add_tcase(s, tc, test_loop_timer_basic, 30);
add_tcase(s, tc, test_loop_timer_precision, 30);
add_tcase(s, tc, test_loop_timer_expire_leak, 30);
add_tcase(s, tc, test_loop_timer_threads, 30);

return s;
}
Expand Down