Index: benchmark/io/http/main.cfa
===================================================================
--- benchmark/io/http/main.cfa	(revision 7affcda14aaec4370f2d650636328f8f1fb39fb2)
+++ benchmark/io/http/main.cfa	(revision 153570dbab5d76eacc3d15ae36b7c7ad38c79538)
@@ -2,4 +2,5 @@
 
 #include <errno.h>
+#include <signal.h>
 #include <stdio.h>
 #include <string.h>
@@ -8,4 +9,5 @@
 	#include <sched.h>
 	#include <signal.h>
+	#include <sys/eventfd.h>
 	#include <sys/socket.h>
 	#include <netinet/in.h>
@@ -177,4 +179,17 @@
 
 //=============================================================================================
+// Termination
+//=============================================================================================
+
+int closefd;
+void cleanstop(int) {
+	eventfd_t buffer = 1;
+	char * buffer_s = (char*)&buffer;
+	int ret = write(closefd, buffer_s, sizeof(buffer));
+	if(ret < 0) abort( "eventfd write error: (%d) %s\n", (int)errno, strerror(errno) );
+	return;
+}
+
+//=============================================================================================
 // Main
 //============================================================================================='
@@ -186,4 +201,18 @@
 	// Parse args
 	parse_options(argc, argv);
+
+	//===================
+	// Setup non-interactive termination
+	if(!options.interactive) {
+		closefd = eventfd(0, 0);
+		if(closefd < 0) abort( "eventfd error: (%d) %s\n", (int)errno, strerror(errno) );
+
+		sighandler_t prev = signal(SIGTERM, cleanstop);
+		intptr_t prev_workaround = (intptr_t) prev;
+		// can't use SIG_ERR it crashes the compiler
+		if(prev_workaround == -1) abort( "signal setup error: (%d) %s\n", (int)errno, strerror(errno) );
+
+		sout | "Signal termination ready";
+	}
 
 	//===================
@@ -257,9 +286,11 @@
 
 		{
+			// Stats printer makes a copy so this needs to persist longer than normal
+			Worker * workers;
 			ServerCluster cl[options.clopts.nclusters];
 
 			init_protocol();
 			{
-				Worker * workers = anew(options.clopts.nworkers);
+				workers = anew(options.clopts.nworkers);
 				cl[0].prnt->workers = workers;
 				cl[0].prnt->worker_cnt = options.clopts.nworkers;
@@ -285,14 +316,20 @@
 				}
 				sout | nl;
-				if(!options.interactive) park();
 				{
-					char buffer[128];
-					for() {
-						int ret = cfa_read(0, buffer, 128, 0);
-						if(ret == 0) break;
+					if(options.interactive) {
+						char buffer[128];
+						for() {
+							int ret = cfa_read(0, buffer, 128, 0);
+							if(ret == 0) break;
+							if(ret < 0) abort( "main read error: (%d) %s\n", (int)errno, strerror(errno) );
+							sout | "User wrote '" | "" | nonl;
+							write(sout, buffer, ret - 1);
+							sout | "'";
+						}
+					}
+					else {
+						char buffer[sizeof(eventfd_t)];
+						int ret = cfa_read(closefd, buffer, sizeof(eventfd_t), 0);
 						if(ret < 0) abort( "main read error: (%d) %s\n", (int)errno, strerror(errno) );
-						sout | "User wrote '" | "" | nonl;
-						write(sout, buffer, ret - 1);
-						sout | "'";
 					}
 
@@ -323,5 +360,7 @@
 
 				sout | "Stopping connection threads..." | nonl; flush( sout );
-				adelete(workers);
+				for(i; options.clopts.nworkers) {
+					join(workers[i]);
+				}
 			}
 			sout | "done";
@@ -330,4 +369,14 @@
 			deinit_protocol();
 			sout | "done";
+
+			sout | "Stopping printer threads..." | nonl; flush( sout );
+			for(i; options.clopts.nclusters) {
+				StatsPrinter * p = cl[i].prnt;
+				if(p) join(*p);
+			}
+			sout | "done";
+
+			// Now that the stats printer is stopped, we can reclaim this
+			adelete(workers);
 
 			sout | "Stopping processors/clusters..." | nonl; flush( sout );
