Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
Angus Salkeld 2012-02-06 22:40:21 +11:00
parent b1e84265cd
commit 09152fda3e
4 changed files with 294 additions and 3 deletions

View File

@ -0,0 +1,98 @@
From 1f269e7bfc916f2ae852e8a7faa591af74c59f05 Mon Sep 17 00:00:00 2001
From: Angus Salkeld <asalkeld@redhat.com>
Date: Mon, 6 Feb 2012 22:31:55 +1100
Subject: [PATCH 1/3] LOOP: fix the todo calculations.
Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
---
lib/loop.c | 22 ++++++++++++----------
lib/loop_job.c | 5 ++++-
2 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/lib/loop.c b/lib/loop.c
index 142e7eb..45ceb3f 100644
--- a/lib/loop.c
+++ b/lib/loop.c
@@ -26,7 +26,7 @@
#include "loop_int.h"
#include "util_int.h"
-static int32_t
+static void
qb_loop_run_level(struct qb_loop_level *level)
{
struct qb_loop_item *job;
@@ -44,13 +44,12 @@ Ill_have_another:
level->todo--;
processed++;
if (level->l->stop_requested) {
- return processed;
+ return;
}
if (processed < level->to_process) {
goto Ill_have_another;
}
}
- return processed;
}
void
@@ -139,20 +138,23 @@ qb_loop_run(struct qb_loop *l)
if (todo > 0) {
ms_timeout = 0;
} else {
- todo = 0;
if (l->timer_source) {
ms_timeout = qb_loop_timer_msec_duration_to_expire(l->timer_source);
} else {
ms_timeout = -1;
}
}
- todo += l->fd_source->poll(l->fd_source, ms_timeout);
-
- for (p = QB_LOOP_HIGH; p >= p_stop; p--) {
- todo -= qb_loop_run_level(&l->level[p]);
- if (l->stop_requested) {
- return;
+ (void)l->fd_source->poll(l->fd_source, ms_timeout);
+
+ todo = 0;
+ for (p = QB_LOOP_HIGH; p >= QB_LOOP_LOW; p--) {
+ if (p >= p_stop) {
+ qb_loop_run_level(&l->level[p]);
+ if (l->stop_requested) {
+ return;
+ }
}
+ todo += l->level[p].todo;
}
} while (!l->stop_requested);
}
diff --git a/lib/loop_job.c b/lib/loop_job.c
index c17663d..123d1af 100644
--- a/lib/loop_job.c
+++ b/lib/loop_job.c
@@ -48,16 +48,19 @@ get_more_jobs(struct qb_loop_source *s, int32_t ms_timeout)
{
int32_t p;
int32_t new_jobs = 0;
+ int32_t level_jobs = 0;
/*
* this is simple, move jobs from wait_head to job_head
*/
for (p = QB_LOOP_LOW; p <= QB_LOOP_HIGH; p++) {
if (!qb_list_empty(&s->l->level[p].wait_head)) {
- new_jobs += qb_list_length(&s->l->level[p].wait_head);
+ level_jobs = qb_list_length(&s->l->level[p].wait_head);
+ new_jobs += level_jobs;
qb_list_splice(&s->l->level[p].wait_head,
&s->l->level[p].job_head);
qb_list_init(&s->l->level[p].wait_head);
+ s->l->level[p].todo += level_jobs;
}
}
return new_jobs;
--
1.7.7.6

View File

@ -0,0 +1,127 @@
From 7d2739fdd23e118bbaa42b3c43381b88ea842c88 Mon Sep 17 00:00:00 2001
From: Angus Salkeld <asalkeld@redhat.com>
Date: Sat, 4 Feb 2012 12:28:45 +1100
Subject: [PATCH 2/3] TEST: check for a single job causing a cpu spin
Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
---
tests/check_loop.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/tests/check_loop.c b/tests/check_loop.c
index 50e80da..eb79a51 100644
--- a/tests/check_loop.c
+++ b/tests/check_loop.c
@@ -60,6 +60,7 @@ static void job_1_r(void *data)
res = qb_loop_job_add(l, QB_LOOP_MED, data, job_2);
ck_assert_int_eq(res, 0);
}
+
static void job_1_add_nuts(void *data)
{
int32_t res;
@@ -153,7 +154,6 @@ START_TEST(test_loop_job_4)
}
END_TEST
-
START_TEST(test_loop_job_nuts)
{
int32_t res;
@@ -169,6 +169,53 @@ START_TEST(test_loop_job_nuts)
}
END_TEST
+static qb_util_stopwatch_t *rl_sw;
+#define RATE_LIMIT_RUNTIME_SEC 3
+
+static void job_add_self(void *data)
+{
+ int32_t res;
+ uint64_t elapsed1;
+ qb_loop_t *l = (qb_loop_t *)data;
+
+ job_1_run_count++;
+ qb_util_stopwatch_stop(rl_sw);
+ elapsed1 = qb_util_stopwatch_us_elapsed_get(rl_sw);
+ if (elapsed1 > (RATE_LIMIT_RUNTIME_SEC * QB_TIME_US_IN_SEC)) {
+ /* run for 3 seconds */
+ qb_loop_stop(l);
+ return;
+ }
+ res = qb_loop_job_add(l, QB_LOOP_MED, data, job_add_self);
+ ck_assert_int_eq(res, 0);
+}
+
+START_TEST(test_job_rate_limit)
+{
+ int32_t res;
+ qb_loop_t *l = qb_loop_create();
+ fail_if(l == NULL);
+
+ rl_sw = qb_util_stopwatch_create();
+ fail_if(rl_sw == NULL);
+
+ qb_util_stopwatch_start(rl_sw);
+
+ res = qb_loop_job_add(l, QB_LOOP_MED, l, job_add_self);
+ ck_assert_int_eq(res, 0);
+
+ qb_loop_run(l);
+ /*
+ * the test is to confirm that a single job does not run away
+ * and cause cpu spin. We are going to say that a spin is more than
+ * one job per 50ms if there is only one job pending in the loop.
+ */
+ _ck_assert_int(job_1_run_count, <, (RATE_LIMIT_RUNTIME_SEC * (QB_TIME_MS_IN_SEC/50)) + 1);
+ qb_loop_destroy(l);
+ qb_util_stopwatch_free(rl_sw);
+}
+END_TEST
+
static Suite *loop_job_suite(void)
{
TCase *tc;
@@ -188,6 +235,12 @@ static Suite *loop_job_suite(void)
tc = tcase_create("run_500");
tcase_add_test(tc, test_loop_job_nuts);
+ tcase_set_timeout(tc, 5);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("rate_limit");
+ tcase_add_test(tc, test_job_rate_limit);
+ tcase_set_timeout(tc, 5);
suite_add_tcase(s, tc);
return s;
@@ -465,12 +518,17 @@ static Suite *loop_timer_suite(void)
tcase_add_test(tc, test_loop_timer_expire_leak);
tcase_set_timeout(tc, 30);
suite_add_tcase(s, tc);
+ return s;
+}
+static Suite *loop_signal_suite(void)
+{
+ TCase *tc;
+ Suite *s = suite_create("loop_signal_suite");
tc = tcase_create("signals");
tcase_add_test(tc, test_loop_sig_handling);
tcase_set_timeout(tc, 10);
suite_add_tcase(s, tc);
-
return s;
}
@@ -479,6 +537,7 @@ int32_t main(void)
int32_t number_failed;
SRunner *sr = srunner_create(loop_job_suite());
srunner_add_suite (sr, loop_timer_suite());
+ srunner_add_suite (sr, loop_signal_suite());
qb_log_init("check", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
--
1.7.7.6

View File

@ -0,0 +1,58 @@
From cf198dc03e51439da9e824abb7acc29c30e691ae Mon Sep 17 00:00:00 2001
From: Angus Salkeld <asalkeld@redhat.com>
Date: Mon, 6 Feb 2012 22:35:30 +1100
Subject: [PATCH 3/3] LOOP: prevent jobs from consuming too much cpu.
Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
---
lib/loop.c | 23 ++++++++++++++++++++---
1 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/lib/loop.c b/lib/loop.c
index 45ceb3f..3e10b89 100644
--- a/lib/loop.c
+++ b/lib/loop.c
@@ -118,6 +118,8 @@ qb_loop_run(struct qb_loop *l)
int32_t p;
int32_t p_stop = QB_LOOP_LOW;
int32_t todo = 0;
+ int32_t job_todo;
+ int32_t timer_todo;
int32_t ms_timeout;
l->stop_requested = QB_FALSE;
@@ -130,13 +132,28 @@ qb_loop_run(struct qb_loop *l)
}
if (l->job_source && l->job_source->poll) {
- todo += l->job_source->poll(l->job_source, 0);
+ job_todo = l->job_source->poll(l->job_source, 0);
+ } else {
+ job_todo = 0;
}
if (l->timer_source && l->timer_source->poll) {
- todo += l->timer_source->poll(l->timer_source, 0);
+ timer_todo = l->timer_source->poll(l->timer_source, 0);
+ } else {
+ timer_todo = 0;
}
- if (todo > 0) {
+ if (todo > 0 || timer_todo > 0) {
+ /*
+ * if there are old todos or timer todos then don't wait.
+ */
ms_timeout = 0;
+ } else if (job_todo > 0) {
+ todo = 0;
+ /*
+ * if we only have jobs to do (not timers or old todos)
+ * then set a non-zero timeout. Jobs can spin out of
+ * control if someone keeps adding them.
+ */
+ ms_timeout = 50;
} else {
if (l->timer_source) {
ms_timeout = qb_loop_timer_msec_duration_to_expire(l->timer_source);
--
1.7.7.6

View File

@ -1,6 +1,6 @@
Name: libqb
Version: 0.9.0
Release: 1%{?dist}
Release: 2%{?dist}
Summary: An IPC library for high performance servers
Group: System Environment/Libraries
@ -9,7 +9,9 @@ URL: http://www.libqb.org
Source0: https://fedorahosted.org/releases/q/u/quarterback/%{name}-%{version}.tar.xz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
#Patch1:
Patch1: 0001-LOOP-fix-the-todo-calculations.patch
Patch2: 0002-TEST-check-for-a-single-job-causing-a-cpu-spin.patch
Patch3: 0003-LOOP-prevent-jobs-from-consuming-too-much-cpu.patch
BuildRequires: libtool doxygen procps check-devel
@ -22,7 +24,9 @@ Initially these are IPC and poll.
%prep
%setup -q
#%patch1 -p1
%patch1 -p1
%patch2 -p1
%patch3 -p1
%build
%configure --disable-static
@ -67,6 +71,10 @@ developing applications that use %{name}.
%{_mandir}/man3/qb*3*
%changelog
* Mon Feb 06 2012 Angus Salkeld <asalkeld@redhat.com> - 0.9.0-2
- Fix a spin in the mainloop when a timer or poll gets removed
When in the job queue (#787196).
* Fri Jan 27 2012 Angus Salkeld <asalkeld@redhat.com> - 0.9.0-1
- Rebased to 0.9.0