Selaa lähdekoodia

Allow timers to add a callback to cleanup resources

bel2125 4 vuotta sitten
vanhempi
commit
8d796925d4

+ 2 - 2
VisualStudio/unit_test/unit_test.vcxproj

@@ -26,7 +26,7 @@
     <ClCompile Include="..\..\unittest\private_exe.c" />
     <ClCompile Include="..\..\unittest\public_func.c" />
     <ClCompile Include="..\..\unittest\public_server.c" />
-    <ClInclude Include="..\..\unittest\timertest.c" />
+    <ClCompile Include="..\..\unittest\timertest.c" />
     <ClCompile Include="..\..\unittest\shared.c" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
@@ -73,7 +73,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>MAIN_PUBLIC_SERVER=main;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>MAIN_TIMER_PRIVATE=main;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\src;$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\..\check-0.10.0\;$(ProjectDir)..\..\..\check-0.10.0\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>

+ 3 - 3
VisualStudio/unit_test/unit_test.vcxproj.filters

@@ -39,9 +39,6 @@
     <ClInclude Include="..\..\unittest\civetweb_check.h">
       <Filter>Headerdateien</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\unittest\timertest.c">
-      <Filter>Quelldateien</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\unittest\public_server.c">
@@ -62,5 +59,8 @@
     <ClCompile Include="..\..\src\civetweb.c">
       <Filter>Quelldateien</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\unittest\timertest.c">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 9 - 4
src/civetweb.c

@@ -6083,6 +6083,10 @@ mg_stat(const struct mg_connection *conn,
 	}
 	memset(filep, 0, sizeof(*filep));
 
+	if (mg_path_suspicious(conn, path)) {
+		return 0;
+	}
+
 	if (0 == stat(path, &st)) {
 		filep->size = (uint64_t)(st.st_size);
 		filep->last_modified = st.st_mtime;
@@ -11385,7 +11389,7 @@ struct process_control_data {
 };
 
 static int
-abort_process(void *data)
+abort_cgi_process(void *data)
 {
 	/* Waitpid checks for child status and won't work for a pid that does
 	 * not identify a child of the current process. Thus, if the pid is
@@ -11530,8 +11534,9 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 		          cgi_timeout /* in seconds */,
 		          0.0,
 		          1,
-		          abort_process,
-		          (void *)proc);
+		          abort_cgi_process,
+		          (void *)proc,
+		          NULL);
 	}
 #endif
 
@@ -11704,7 +11709,7 @@ done:
 	mg_free(blk.buf);
 
 	if (pid != (pid_t)-1) {
-		abort_process((void *)proc);
+		abort_cgi_process((void *)proc);
 	}
 
 	if (fdin[0] != -1) {

+ 14 - 2
src/mod_lua.inl

@@ -2038,6 +2038,13 @@ lua_action(struct laction_arg *arg)
 }
 
 
+static void
+lua_action_cancel(struct laction_arg *arg)
+{
+	mg_free(arg);
+}
+
+
 static int
 lwebsocket_set_timer(lua_State *L, int is_periodic)
 {
@@ -2089,8 +2096,13 @@ lwebsocket_set_timer(lua_State *L, int is_periodic)
 		arg->txt[action_txt_len + 7] = ')';
 		arg->txt[action_txt_len + 8] = 0;
 		if (0
-		    == timer_add(
-		           ctx, timediff, is_periodic, 1, lua_action, (void *)arg)) {
+		    == timer_add(ctx,
+		                 timediff,
+		                 is_periodic,
+		                 1,
+		                 lua_action,
+		                 (void *)arg,
+		                 lua_action_cancel)) {
 			/* Timer added successfully */
 			ok = 1;
 		}

+ 19 - 5
src/timer.inl

@@ -12,12 +12,14 @@
 #endif
 
 typedef int (*taction)(void *arg);
+typedef void (*tcancelaction)(void *arg);
 
 struct ttimer {
 	double time;
 	double period;
 	taction action;
 	void *arg;
+	tcancelaction cancel;
 };
 
 struct ttimers {
@@ -70,7 +72,8 @@ timer_add(struct mg_context *ctx,
           double period,
           int is_relative,
           taction action,
-          void *arg)
+          void *arg,
+          tcancelaction cancel)
 {
 	int error = 0;
 	double now;
@@ -130,6 +133,7 @@ timer_add(struct mg_context *ctx,
 		ctx->timers->timers[u].period = period;
 		ctx->timers->timers[u].action = action;
 		ctx->timers->timers[u].arg = arg;
+		ctx->timers->timers[u].cancel = cancel;
 		ctx->timers->timer_count++;
 	}
 	pthread_mutex_unlock(&ctx->timers->mutex);
@@ -178,10 +182,18 @@ timer_thread_run(void *thread_func_param)
 			/* action_res == 0: do not reschedule, free(arg) */
 			if ((action_res > 0) && (t.period > 0)) {
 				/* Should schedule timer again */
-				timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg);
+				timer_add(ctx,
+				          t.time + t.period,
+				          t.period,
+				          0,
+				          t.action,
+				          t.arg,
+				          t.cancel);
 			} else {
-				/* Free timer argument */
-				mg_free(t.arg);
+				/* Allow user to free timer argument */
+				if (t.cancel != NULL) {
+					t.cancel(t.arg);
+				}
 			}
 			continue;
 		} else {
@@ -200,7 +212,9 @@ timer_thread_run(void *thread_func_param)
 	/* Remove remaining timers */
 	for (u = 0; u < ctx->timers->timer_count; u++) {
 		t = ctx->timers->timers[u];
-		mg_free(t.arg);
+		if (t.cancel != NULL) {
+			t.cancel(t.arg);
+		}
 	}
 }
 

+ 74 - 27
unittest/timertest.c

@@ -44,8 +44,10 @@
 
 #include "timertest.h"
 
+#define TIMERS_IN_TEST 10
 static int action_dec_ret;
 
+
 static int
 action_dec(void *arg)
 {
@@ -61,6 +63,23 @@ action_dec(void *arg)
 }
 
 
+static void
+action_cancel(void *arg)
+{
+	int *p = (int *)arg;
+
+	/* test convention: store cancel counter after timer counter */
+	p += TIMERS_IN_TEST;
+
+	if (*p != 0) {
+		ck_abort_msg("Double call of timer cancel action");
+		/* return 0 here would be unreachable code */
+	}
+
+	(*p)++;
+}
+
+
 static int
 action_dec_to_0(void *arg)
 {
@@ -79,7 +98,7 @@ action_dec_to_0(void *arg)
 START_TEST(test_timer_cyclic)
 {
 	struct mg_context ctx;
-	int c[10];
+	int c[TIMERS_IN_TEST * 2];
 	memset(&ctx, 0, sizeof(ctx));
 	memset(c, 0, sizeof(c));
 
@@ -91,11 +110,11 @@ START_TEST(test_timer_cyclic)
 	mark_point();
 
 	c[0] = 100;
-	timer_add(&ctx, 0.05, 0.1, 1, action_dec, c + 0);
+	timer_add(&ctx, 0.05, 0.1, 1, action_dec, c + 0, action_cancel);
 	c[2] = 20;
-	timer_add(&ctx, 0.25, 0.5, 1, action_dec, c + 2);
+	timer_add(&ctx, 0.25, 0.5, 1, action_dec, c + 2, action_cancel);
 	c[1] = 50;
-	timer_add(&ctx, 0.1, 0.2, 1, action_dec, c + 1);
+	timer_add(&ctx, 0.1, 0.2, 1, action_dec, c + 1, action_cancel);
 
 	mark_point();
 
@@ -113,6 +132,10 @@ START_TEST(test_timer_cyclic)
 
 	mark_point();
 
+	mg_sleep(2000); /* Sleep 2 second - timers will not run */
+
+	mark_point();
+
 	/* If this test runs in a virtual environment, like the CI unit test
 	 * containers, there might be some timing deviations, so check the
 	 * counter with some tolerance. */
@@ -123,6 +146,11 @@ START_TEST(test_timer_cyclic)
 	ck_assert_int_le(c[1], +1);
 	ck_assert_int_ge(c[2], -1);
 	ck_assert_int_le(c[2], +1);
+
+	/* Every cancel action must be called once */
+	ck_assert_int_eq(c[0 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[1 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[2 + TIMERS_IN_TEST], 1);
 }
 END_TEST
 
@@ -130,7 +158,7 @@ END_TEST
 START_TEST(test_timer_oneshot_by_callback_retval)
 {
 	struct mg_context ctx;
-	int c[10];
+	int c[TIMERS_IN_TEST * 2];
 	memset(&ctx, 0, sizeof(ctx));
 	memset(c, 0, sizeof(c));
 
@@ -142,11 +170,11 @@ START_TEST(test_timer_oneshot_by_callback_retval)
 	mark_point();
 
 	c[0] = 10;
-	timer_add(&ctx, 0, 0.1, 1, action_dec, c + 0);
+	timer_add(&ctx, 0, 0.1, 1, action_dec, c + 0, action_cancel);
 	c[2] = 2;
-	timer_add(&ctx, 0, 0.5, 1, action_dec, c + 2);
+	timer_add(&ctx, 0, 0.5, 1, action_dec, c + 2, action_cancel);
 	c[1] = 5;
-	timer_add(&ctx, 0, 0.2, 1, action_dec, c + 1);
+	timer_add(&ctx, 0, 0.2, 1, action_dec, c + 1, action_cancel);
 
 	mark_point();
 
@@ -163,11 +191,20 @@ START_TEST(test_timer_oneshot_by_callback_retval)
 	timers_exit(&ctx);
 
 	mark_point();
-	mg_sleep(100);
 
+	mg_sleep(2000); /* Sleep 2 second - timers will not run */
+
+	mark_point();
+
+	/* Every decrement action must be called once */
 	ck_assert_int_eq(c[0], 9);
 	ck_assert_int_eq(c[1], 4);
 	ck_assert_int_eq(c[2], 1);
+
+	/* Every cancel action must be called once */
+	ck_assert_int_eq(c[0 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[1 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[2 + TIMERS_IN_TEST], 1);
 }
 END_TEST
 
@@ -175,7 +212,7 @@ END_TEST
 START_TEST(test_timer_oneshot_by_timer_add)
 {
 	struct mg_context ctx;
-	int c[10];
+	int c[TIMERS_IN_TEST * 2];
 	memset(&ctx, 0, sizeof(ctx));
 	memset(c, 0, sizeof(c));
 
@@ -187,11 +224,11 @@ START_TEST(test_timer_oneshot_by_timer_add)
 	mark_point();
 
 	c[0] = 10;
-	timer_add(&ctx, 0, 0, 1, action_dec, c + 0);
+	timer_add(&ctx, 0, 0, 1, action_dec, c + 0, action_cancel);
 	c[2] = 2;
-	timer_add(&ctx, 0, 0, 1, action_dec, c + 2);
+	timer_add(&ctx, 0, 0, 1, action_dec, c + 2, action_cancel);
 	c[1] = 5;
-	timer_add(&ctx, 0, 0, 1, action_dec, c + 1);
+	timer_add(&ctx, 0, 0, 1, action_dec, c + 1, action_cancel);
 
 	mark_point();
 
@@ -208,11 +245,20 @@ START_TEST(test_timer_oneshot_by_timer_add)
 	timers_exit(&ctx);
 
 	mark_point();
-	mg_sleep(100);
 
+	mg_sleep(2000); /* Sleep 2 second - timers will not run */
+
+	mark_point();
+
+	/* Every decrement action was called once */
 	ck_assert_int_eq(c[0], 9);
 	ck_assert_int_eq(c[1], 4);
 	ck_assert_int_eq(c[2], 1);
+
+	/* Every cancel action must be called once */
+	ck_assert_int_eq(c[0 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[1 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[2 + TIMERS_IN_TEST], 1);
 }
 END_TEST
 
@@ -220,7 +266,7 @@ END_TEST
 START_TEST(test_timer_mixed)
 {
 	struct mg_context ctx;
-	int c[10];
+	int c[TIMERS_IN_TEST];
 	memset(&ctx, 0, sizeof(ctx));
 	memset(c, 0, sizeof(c));
 
@@ -231,35 +277,35 @@ START_TEST(test_timer_mixed)
 
 	/* 3 --> 2, because it is a single shot timer */
 	c[0] = 3;
-	timer_add(&ctx, 0, 0, 1, action_dec_to_0, &c[0]);
+	timer_add(&ctx, 0, 0, 1, action_dec_to_0, &c[0], NULL);
 
 	/* 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]);
+	timer_add(&ctx, 0, 0.2, 1, action_dec_to_0, &c[1], NULL);
 
 	/* 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]);
+	timer_add(&ctx, 0, 0.75, 1, action_dec_to_0, &c[2], NULL);
 
 	/* 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]);
+	timer_add(&ctx, 0, 2.5, 1, action_dec_to_0, &c[3], NULL);
 
 	/* 3 --> 3, will not run at start */
 	c[4] = 3;
-	timer_add(&ctx, 2.5, 0.1, 1, action_dec_to_0, &c[4]);
+	timer_add(&ctx, 2.5, 0.1, 1, action_dec_to_0, &c[4], NULL);
 
 	/* 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]);
+	timer_add(&ctx, -123.456, 2.5, 0, action_dec_to_0, &c[5], NULL);
 
 	/* 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]);
+	timer_add(&ctx, -123.456, 0.75, 0, action_dec_to_0, &c[6], NULL);
 
 	mark_point();
 
@@ -276,7 +322,10 @@ START_TEST(test_timer_mixed)
 	timers_exit(&ctx);
 
 	mark_point();
-	mg_sleep(100);
+
+	mg_sleep(2000); /* Sleep 2 second - timers will not run */
+
+	mark_point();
 
 	ck_assert_int_eq(c[0], 2);
 	ck_assert_int_eq(c[1], 0);
@@ -314,14 +363,12 @@ make_timertest_suite(void)
 
 	return suite;
 }
-#endif
 
+#else
 
-#ifdef REPLACE_CHECK_FOR_LOCAL_DEBUGGING
 /* Used to debug test cases without using the check framework */
-
 void
-TIMER_PRIVATE(void)
+MAIN_TIMER_PRIVATE(void)
 {
 	unsigned f_avail;
 	unsigned f_ret;