Browse Source

Add mixed timer test

bel 8 năm trước cách đây
mục cha
commit
a7644622f5
3 tập tin đã thay đổi với 121 bổ sung12 xóa
  1. 25 11
      src/timer.inl
  2. 2 1
      test/CMakeLists.txt
  3. 94 0
      test/timertest.c

+ 25 - 11
src/timer.inl

@@ -30,16 +30,16 @@ timer_add(struct mg_context *ctx,
 {
 	unsigned u, v;
 	int error = 0;
-	struct timespec now;
-	double dt; /* double time */
+	struct timespec now_ts; /* now in timespec */
+	double now_d;           /* now in double */
 
 	if (ctx->stop_flag) {
 		return 0;
 	}
 
-	clock_gettime(CLOCK_MONOTONIC, &now);
-	dt = (double)(now.tv_sec);
-	dt += (double)(now.tv_nsec) * 1.0E-9;
+	clock_gettime(CLOCK_MONOTONIC, &now_ts);
+	now_d = (double)(now_ts.tv_sec);
+	now_d += (double)(now_ts.tv_nsec) * 1.0E-9;
 
 	/* HCP24: if is_relative = 0 and next_time < now
 	 *        action will be called so fast as possible
@@ -49,19 +49,26 @@ timer_add(struct mg_context *ctx,
 	 *        then the period is working
 	 * Solution:
 	 *        if next_time < now then we set next_time = now.
-	 *        The first callback will be so fast as possible  (now)
+	 *        The first callback will be so fast as possible (now)
 	 *        but the next callback on period
 	*/
 	if (is_relative) {
-		next_time += dt;
-	} else if (next_time < dt) {
-		next_time = dt;
+		next_time += now_d;
+	}
+
+	/* You can not set timers into the past */
+	if (next_time < now_d) {
+		next_time = now_d;
 	}
 
 	pthread_mutex_lock(&ctx->timers->mutex);
 	if (ctx->timers->timer_count == MAX_TIMERS) {
 		error = 1;
 	} else {
+		/* Insert new timer into a sorted list. */
+		/* The linear list is still most efficient for short lists (small
+		 * number of timers) - if there are many timers, different
+		 * algorithms will work better. */
 		for (u = 0; u < ctx->timers->timer_count; u++) {
 			if (ctx->timers->timers[u].time > next_time) {
 				/* HCP24: moving all timers > next_time */
@@ -110,7 +117,8 @@ timer_thread_run(void *thread_func_param)
 	d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
 	while (ctx->stop_flag == 0) {
 		pthread_mutex_lock(&ctx->timers->mutex);
-		if (ctx->timers->timer_count > 0 && d >= ctx->timers->timers[0].time) {
+		if ((ctx->timers->timer_count > 0)
+		    && (d >= ctx->timers->timers[0].time)) {
 			t = ctx->timers->timers[0];
 			for (u = 1; u < ctx->timers->timer_count; u++) {
 				ctx->timers->timers[u - 1] = ctx->timers->timers[u];
@@ -125,7 +133,13 @@ timer_thread_run(void *thread_func_param)
 		} else {
 			pthread_mutex_unlock(&ctx->timers->mutex);
 		}
-		mg_sleep(1);
+
+		/* 10 ms seems reasonable.
+		 * A faster loop (smaller sleep value) increases CPU load,
+		 * a slower loop (higher sleep value) decreases timer accuracy.
+		 */
+		mg_sleep(10);
+
 		clock_gettime(CLOCK_MONOTONIC, &now);
 		d = (double)now.tv_sec + (double)now.tv_nsec * 1.0E-9;
 	}

+ 2 - 1
test/CMakeLists.txt

@@ -174,8 +174,9 @@ civetweb_add_test(PublicServer "Error handling")
 civetweb_add_test(PublicServer "Limit speed")
 
 # Timer tests
-civetweb_add_test(Timer "Timer Periodic")
 civetweb_add_test(Timer "Timer Single Shot")
+civetweb_add_test(Timer "Timer Periodic")
+civetweb_add_test(Timer "Timer Mixed")
 
 # Tests with main.c
 #civetweb_add_test(EXE "Helper funcs")

+ 94 - 0
test/timertest.c

@@ -61,6 +61,20 @@ action_dec(void *arg)
 }
 
 
+static action_dec_to_0(void *arg)
+{
+	int *p = (int *)arg;
+	(*p)--;
+
+	if (*p <= -1) {
+		ck_abort_msg("Periodic timer called too often");
+		/* return 0 here would be unreachable code */
+	}
+
+	return (*p > 0);
+}
+
+
 START_TEST(test_timer_cyclic)
 {
 	struct mg_context ctx;
@@ -210,6 +224,80 @@ START_TEST(test_timer_oneshot_by_timer_add)
 END_TEST
 
 
+START_TEST(test_timer_mixed)
+{
+	struct mg_context ctx;
+	int c[10];
+	memset(&ctx, 0, sizeof(ctx));
+	memset(c, 0, sizeof(c));
+
+	action_dec_ret = 0;
+
+	mark_point();
+	timers_init(&ctx);
+	mg_sleep(100);
+	mark_point();
+
+	/* 3 --> 2, because it is a single shot timer */
+	c[0] = 3;
+	timer_add(&ctx, 0, 0, 1, action_dec_to_0, c + 0);
+
+	/* 3 --> 0, because it will run until c[1] = 0 and then stop */
+	c[1] = 3;
+	timer_add(&ctx, 0, 0.2, 1, action_dec_to_0, c + 1);
+
+	/* 3 --> 1, with 750 ms period, it will run once at start,
+	 * then once 750 ms later, but not 1500 ms later, since the
+	 * timer is already stopped then. */
+	c[2] = 3;
+	timer_add(&ctx, 0, 0.75, 1, action_dec_to_0, c + 2);
+
+	/* 3 --> 2, will run at start, but no cyclic in 1 second */
+	c[3] = 3;
+	timer_add(&ctx, 0, 2.5, 1, action_dec_to_0, c + 3);
+
+	/* 3 --> 3, will not run at start */
+	c[4] = 3;
+	timer_add(&ctx, 2.5, 0.1, 1, action_dec_to_0, c + 4);
+
+	/* 3 --> 2, an absolute timer in the past (123.456) will still
+	 * run once at start, and then with the period */
+	c[5] = 3;
+	timer_add(&ctx, 123.456, 2.5, 0, action_dec_to_0, c + 5);
+
+	/* 3 --> 1, an absolute timer in the past (123.456) will still
+	 * run once at start, and then with the period */
+	c[6] = 3;
+	timer_add(&ctx, 123.456, 0.75, 0, action_dec_to_0, c + 6);
+
+	mark_point();
+
+	mg_sleep(1000); /* Sleep 1 second - timer will run */
+
+	mark_point();
+	ctx.stop_flag = 99; /* End timer thread */
+	mark_point();
+
+	mg_sleep(1000); /* Sleep 1 second - timer will not run */
+
+	mark_point();
+
+	timers_exit(&ctx);
+
+	mark_point();
+	mg_sleep(100);
+
+	ck_assert_int_eq(c[0], 2);
+	ck_assert_int_eq(c[1], 0);
+	ck_assert_int_eq(c[2], 1);
+	ck_assert_int_eq(c[3], 2);
+	ck_assert_int_eq(c[4], 3);
+	ck_assert_int_eq(c[5], 2);
+	ck_assert_int_eq(c[6], 1);
+}
+END_TEST
+
+
 Suite *
 make_timertest_suite(void)
 {
@@ -217,6 +305,7 @@ make_timertest_suite(void)
 
 	TCase *const tcase_timer_cyclic = tcase_create("Timer Periodic");
 	TCase *const tcase_timer_oneshot = tcase_create("Timer Single Shot");
+	TCase *const tcase_timer_mixed = tcase_create("Timer Mixed");
 
 	tcase_add_test(tcase_timer_cyclic, test_timer_cyclic);
 	tcase_set_timeout(tcase_timer_cyclic, 30);
@@ -227,6 +316,10 @@ make_timertest_suite(void)
 	tcase_set_timeout(tcase_timer_oneshot, 30);
 	suite_add_tcase(suite, tcase_timer_oneshot);
 
+	tcase_add_test(tcase_timer_mixed, test_timer_mixed);
+	tcase_set_timeout(tcase_timer_mixed, 30);
+	suite_add_tcase(suite, tcase_timer_mixed);
+
 	return suite;
 }
 
@@ -252,6 +345,7 @@ TIMER_PRIVATE(void)
 	test_timer_cyclic(0);
 	test_timer_oneshot_by_timer_add(0);
 	test_timer_oneshot_by_callback_retval(0);
+	test_timer_mixed(0);
 
 	mg_exit_library();