Index: benchmark/io/http/filecache.cfa
===================================================================
--- benchmark/io/http/filecache.cfa	(revision e93cbfad0c39d46659a181cff11c834506dd270a)
+++ benchmark/io/http/filecache.cfa	(revision 289a21c8dd17d19251b1aa7b6e997f0ebfef561a)
@@ -82,5 +82,5 @@
 
 [int fd, size_t size] get_file( * const char file, size_t len ) {
-	uint32_t idx = murmur3_32( (const uint8_t *)file, len, options.hash_seed ) % file_cache.size;
+	uint32_t idx = murmur3_32( (const uint8_t *)file, len, options.file_cache.hash_seed ) % file_cache.size;
 
 	for(int i = 0;; i++) {
@@ -99,5 +99,5 @@
 
 int put_file( cache_line & entry ) {
-	uint32_t idx = murmur3_32( (const uint8_t *)entry.file, strlen(entry.file), options.hash_seed ) % file_cache.size;
+	uint32_t idx = murmur3_32( (const uint8_t *)entry.file, strlen(entry.file), options.file_cache.hash_seed ) % file_cache.size;
 
 	int i = 0;
@@ -136,6 +136,6 @@
 		raw[idx].file = strdup(fpath+2);
 		raw[idx].size = sb->st_size;
-		if( !options.file_cache_list ) {
-			raw[idx].fd = open( fpath, options.open_flags );
+		if( !options.file_cache.list ) {
+			raw[idx].fd = open( fpath, options.file_cache.open_flags );
 			if(raw[idx].fd < 0) {
 				abort( "open file error: (%d) %s\n", (int)errno, strerror(errno) );
@@ -154,5 +154,26 @@
 	}
 
-	if(options.file_cache_list) {
+	// Step 2 create the cache
+	file_cache.size = options.file_cache.size > 0 ? options.file_cache.size : fsize;
+	if( file_cache.size < fcount ) {
+		abort("File Cache too small\n");
+	}
+
+	file_cache.entries = anew(file_cache.size);
+
+	// Step 3 fill the cache
+	int conflicts = 0;
+	for(i; fcount) {
+		conflicts += put_file( raw[i] );
+	}
+	printf("Filled cache from path \"%s\" with %zu files\n", path, fcount);
+	if( conflicts > 0 ) {
+		printf("Found %d conflicts (seed: %u)\n", conflicts, options.file_cache.hash_seed);
+		#if defined(REJECT_CONFLICTS)
+			abort("Conflicts found in the cache");
+		#endif
+	}
+
+	if(options.file_cache.list) {
 		printf("Listing files and exiting\n");
 		for(i; fcount) {
@@ -163,27 +184,6 @@
 		}
 		free(raw);
+		adelete(file_cache.size, file_cache.entries);
 		exit(0);
-	}
-
-	// Step 2 create the cache
-	file_cache.size = options.file_cache_size > 0 ? options.file_cache_size : fsize;
-	if( file_cache.size < fcount ) {
-		abort("File Cache too small\n");
-	}
-
-	file_cache.entries = anew(fsize);
-
-	// Step 3 fill the cache
-	int conflicts = 0;
-	for(i; fcount) {
-		printf("Added file %s\n", raw[i].file);
-		conflicts += put_file( raw[i] );
-	}
-	printf("Filled cache from path \"%s\" with %zu files\n", path, fcount);
-	if( conflicts > 0 ) {
-		printf("Found %d conflicts (seed: %u)\n", conflicts, options.hash_seed);
-		#if defined(REJECT_CONFLICTS)
-			abort("Conflicts found in the cache");
-		#endif
 	}
 
Index: benchmark/io/http/main.cfa
===================================================================
--- benchmark/io/http/main.cfa	(revision e93cbfad0c39d46659a181cff11c834506dd270a)
+++ benchmark/io/http/main.cfa	(revision 289a21c8dd17d19251b1aa7b6e997f0ebfef561a)
@@ -17,5 +17,4 @@
 #include "filecache.hfa"
 #include "options.hfa"
-#include "parseargs.hfa"
 #include "worker.hfa"
 
@@ -23,14 +22,4 @@
 // Globals
 //=============================================================================================
-Options options @= {
-	0,     //   open_flags;
-	42u,   // 	hash_seed;
-	0,     //   file_cache_size;
-	false, // 	file_cache_list;
-	false, // 	procstats;
-	false, // 	viewhalts;
-	0      // 	the_cluster;
-};
-
 channel & wait_connect;
 
@@ -40,12 +29,12 @@
 
 void ?{}( ServerProc & this ) {
-	/* paranoid */ assert( options.the_cluster != 0p );
-	(this.self){ "Benchmark Processor", *options.the_cluster };
+	/* paranoid */ assert( options.clopts.instance != 0p );
+	(this.self){ "Benchmark Processor", *options.clopts.instance };
 
 	#if !defined(__CFA_NO_STATISTICS__)
-		if( options.procstats ) {
-			print_stats_at_exit( this.self, options.the_cluster->print_stats );
+		if( options.clopts.procstats ) {
+			print_stats_at_exit( this.self, options.clopts.instance->print_stats );
 		}
-		if( options.viewhalts ) {
+		if( options.clopts.viewhalts ) {
 			print_halts( this.self );
 		}
@@ -57,36 +46,7 @@
 //============================================================================================='
 int main( int argc, char * argv[] ) {
-	int port      = 8080;
-	int backlog   = 10;
-	int nprocs    = 1;
-	int nworkers  = 1;
-	int cl_flags  = 0;
-	int chan_size = 10;
-	const char * path = ".";
 	//===================
 	// Parse args
-	static cfa_option opt[] = {
-		{'p', "port", "Port the server will listen on", port},
-		{'c', "cpus", "Number of processors to use", nprocs},
-		{'t', "threads", "Number of worker threads to use", nworkers},
-		{'b', "accept-backlog", "Maximum number of pending accepts", backlog},
-		{'B', "channel-size", "Maximum number of accepted connection pending", chan_size},
-		{'S', "seed", "seed to use for hashing", options.hash_seed },
-		{'C', "cache-size", "Size of the cache to use, if set to small, will uses closes power of 2", options.file_cache_size },
-		{'l', "list-files", "List the files in the specified path and exit", options.file_cache_list, parse_settrue }
-
-	};
-	int opt_cnt = sizeof(opt) / sizeof(cfa_option);
-
-	char **left;
-      parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left );
-	if( left[0] != 0p ) {
-		path = left[0];
-		left++;
-	}
-	if( left[0] != 0p ) {
-		abort("Too many trailing arguments!\n");
-	}
-
+	const char * path = parse_options(argc, argv);
 
 	//===================
@@ -97,5 +57,5 @@
 	//===================
 	// Open Socket
-	printf("Listening on port %d\n", port);
+	printf("Listening on port %d\n", options.socket.port);
 	int server_fd = socket(AF_INET, SOCK_STREAM, 0);
 	if(server_fd < 0) {
@@ -109,5 +69,5 @@
 	address.sin_family = AF_INET;
 	address.sin_addr.s_addr = htonl(INADDR_ANY);
-	address.sin_port = htons( port );
+	address.sin_port = htons( options.socket.port );
 
 	ret = bind( server_fd, (struct sockaddr *)&address, sizeof(address) );
@@ -116,5 +76,5 @@
 	}
 
-	ret = listen( server_fd, backlog );
+	ret = listen( server_fd, options.socket.backlog );
 	if(ret < 0) {
 		abort( "listen error: (%d) %s\n", (int)errno, strerror(errno) );
@@ -124,28 +84,36 @@
 	// Run Server Cluster
 	{
-		cluster cl = { "Server Cluster", cl_flags };
+		cluster cl = { "Server Cluster", options.clopts.flags };
 		#if !defined(__CFA_NO_STATISTICS__)
 			print_stats_at_exit( cl, CFA_STATS_READY_Q | CFA_STATS_IO );
 		#endif
-		options.the_cluster = &cl;
+		options.clopts.instance = &cl;
 
-		channel chan = { chan_size };
+		channel chan = { options.clopts.chan_size };
 		&wait_connect = &chan;
 
 		{
-			ServerProc procs[nprocs];
+			ServerProc procs[options.clopts.nprocs];
 			{
-				Worker workers[nworkers];
-				printf("%d workers started on %d processors\n", nworkers, nprocs);
+				Worker workers[options.clopts.nworkers];
+				printf("%d workers started on %d processors\n", options.clopts.nworkers, options.clopts.nprocs);
 				{
 					Acceptor acceptor = { server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen, 0 };
+
+					char buffer[128];
+					while(!feof(stdin)) {
+						fgets(buffer, 128, stdin);
+					}
+
+					printf("Shutting Down\n");
 				}
-				printf("Shutting Down\n");
+				printf("Acceptor Closed\n");
 
 				// Clean-up the workers
-				for(nworkers) {
+				for(options.clopts.nworkers) {
 					put( wait_connect, -1 );
 				}
 			}
+			printf("Workers Closed\n");
 		}
 	}
Index: benchmark/io/http/options.cfa
===================================================================
--- benchmark/io/http/options.cfa	(revision 289a21c8dd17d19251b1aa7b6e997f0ebfef561a)
+++ benchmark/io/http/options.cfa	(revision 289a21c8dd17d19251b1aa7b6e997f0ebfef561a)
@@ -0,0 +1,111 @@
+#include "options.hfa"
+
+#define _GNU_SOURCE
+#define __USE_GNU
+extern "C" {
+	#include <sys/types.h>
+	#include <sys/stat.h>
+	#include <fcntl.h>
+}
+
+#include <kernel.hfa>
+
+#include "parseargs.hfa"
+
+
+Options options @= {
+	{ // file_cache
+		0,     // open_flags;
+		42u,   // hash_seed;
+		0,     // size;
+		false, // list;
+		false  // fixed_fds
+	},
+
+	{ // socket
+		8080, // port
+		10    // backlog
+	},
+
+	{ // cluster
+		1,     // nprocs;
+		1,     // nworkers;
+		0,     // flags;
+		10,    // chan_size;
+		false, // procstats
+		false, // viewhalts
+		0,     // instance;
+	}
+};
+
+const char * parse_options( int argc, char * argv[] ) {
+	bool uthrdpo = false;
+	bool subthrd = false;
+	bool eagrsub = false;
+	bool fixedfd = false;
+	bool sqkpoll = false;
+	bool iokpoll = false;
+	unsigned sublen = 16;
+
+	static cfa_option opt[] = {
+		{'p', "port",           "Port the server will listen on", options.socket.port},
+		{'c', "cpus",           "Number of processors to use", options.clopts.nprocs},
+		{'t', "threads",        "Number of worker threads to use", options.clopts.nworkers},
+		{'b', "accept-backlog", "Maximum number of pending accepts", options.socket.backlog},
+		{'B', "channel-size",   "Maximum number of accepted connection pending", options.clopts.chan_size},
+		{'S', "seed",           "seed to use for hashing", options.file_cache.hash_seed },
+		{'C', "cache-size",     "Size of the cache to use, if set to small, will uses closes power of 2", options.file_cache.size },
+		{'l', "list-files",     "List the files in the specified path and exit", options.file_cache.list, parse_settrue },
+		{'u', "userthread",     "If set, cluster uses user-thread to poll I/O", uthrdpo, parse_settrue },
+		{'s', "submitthread",   "If set, cluster uses polling thread to submit I/O", subthrd, parse_settrue },
+		{'e', "eagersubmit",    "If set, cluster submits I/O eagerly but still aggregates submits", eagrsub, parse_settrue},
+		{'f', "fixed-fds",      "If set, files are open eagerly and pre-registered with the cluster", fixedfd, parse_settrue},
+		{'k', "kpollsubmit",    "If set, cluster uses IORING_SETUP_SQPOLL, implies -f", sqkpoll, parse_settrue },
+		{'i', "kpollcomplete",  "If set, cluster uses IORING_SETUP_IOPOLL", iokpoll, parse_settrue },
+		{'L', "submitlength",   "Max number of submitions that can be submitted together", sublen },
+
+	};
+	int opt_cnt = sizeof(opt) / sizeof(cfa_option);
+
+	char **left;
+	parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left );
+
+	if( uthrdpo ) {
+		options.clopts.flags |= CFA_CLUSTER_IO_POLLER_USER_THREAD;
+	}
+
+	if( subthrd ) {
+		options.clopts.flags |= CFA_CLUSTER_IO_POLLER_THREAD_SUBMITS;
+	}
+
+	if( eagrsub ) {
+		options.clopts.flags |= CFA_CLUSTER_IO_EAGER_SUBMITS;
+	}
+
+	if( fixedfd ) {
+		options.file_cache.fixed_fds = true;
+	}
+
+	if( sqkpoll ) {
+		options.clopts.flags |= CFA_CLUSTER_IO_KERNEL_POLL_SUBMITS;
+		options.file_cache.fixed_fds = true;
+	}
+
+	if( iokpoll ) {
+		options.clopts.flags |= CFA_CLUSTER_IO_KERNEL_POLL_COMPLETES;
+		options.file_cache.open_flags |= O_DIRECT;
+	}
+
+	options.clopts.flags |= (sublen << CFA_CLUSTER_IO_BUFFLEN_OFFSET);
+
+	if( left[0] == 0p ) { return "."; }
+
+	const char * path = left[0];
+	left++;
+
+	if( left[0] != 0p ) {
+		abort("Too many trailing arguments!\n");
+	}
+
+	return path;
+}
Index: benchmark/io/http/options.hfa
===================================================================
--- benchmark/io/http/options.hfa	(revision e93cbfad0c39d46659a181cff11c834506dd270a)
+++ benchmark/io/http/options.hfa	(revision 289a21c8dd17d19251b1aa7b6e997f0ebfef561a)
@@ -6,12 +6,29 @@
 
 struct Options {
-	int open_flags;
-	uint32_t hash_seed;
-	size_t file_cache_size;
-	bool file_cache_list;
-	bool procstats;
-	bool viewhalts;
-	cluster * the_cluster;
+	struct {
+		int open_flags;
+		uint32_t hash_seed;
+		size_t size;
+		bool list;
+		bool fixed_fds;
+	} file_cache;
+
+	struct {
+		int port;
+		int backlog;
+	} socket;
+
+	struct {
+		int nprocs;
+		int nworkers;
+		int flags;
+		int chan_size;
+		bool procstats;
+		bool viewhalts;
+		cluster * instance;
+	} clopts;
 };
 
 extern Options options;
+
+const char * parse_options( int argc, char * argv[] );
Index: benchmark/io/http/protocol.cfa
===================================================================
--- benchmark/io/http/protocol.cfa	(revision e93cbfad0c39d46659a181cff11c834506dd270a)
+++ benchmark/io/http/protocol.cfa	(revision 289a21c8dd17d19251b1aa7b6e997f0ebfef561a)
@@ -25,5 +25,19 @@
 };
 
-_Static_assert( KNOWN_CODES == (sizeof(http_msgs) / sizeof(http_msgs[0])));
+_Static_assert( KNOWN_CODES == (sizeof(http_msgs ) / sizeof(http_msgs [0])));
+
+const int http_codes[] = {
+	200,
+	400,
+	404,
+	413,
+	414,
+};
+
+_Static_assert( KNOWN_CODES == (sizeof(http_codes) / sizeof(http_codes[0])));
+
+int code_val(HttpCode code) {
+	return http_codes[code];
+}
 
 static inline int answer( int fd, const char * it, int len) {
@@ -61,6 +75,6 @@
 	for() {
 		int ret = cfa_read(fd, it, count);
+		if(ret == 0 ) return [OK200, true, 0p, 0];
 		if(ret < 0 ) {
-			if( errno ) return [OK200, true, 0p, 0];
 			if( errno == EAGAIN || errno == EWOULDBLOCK) continue READ;
 			abort( "read error: (%d) %s\n", (int)errno, strerror(errno) );
Index: benchmark/io/http/protocol.hfa
===================================================================
--- benchmark/io/http/protocol.hfa	(revision e93cbfad0c39d46659a181cff11c834506dd270a)
+++ benchmark/io/http/protocol.hfa	(revision 289a21c8dd17d19251b1aa7b6e997f0ebfef561a)
@@ -10,4 +10,6 @@
 };
 
+int code_val(HttpCode code);
+
 int answer_error( int fd, HttpCode code );
 int answer_header( int fd, size_t size );
Index: benchmark/io/http/worker.cfa
===================================================================
--- benchmark/io/http/worker.cfa	(revision e93cbfad0c39d46659a181cff11c834506dd270a)
+++ benchmark/io/http/worker.cfa	(revision 289a21c8dd17d19251b1aa7b6e997f0ebfef561a)
@@ -26,5 +26,5 @@
 //=============================================================================================
 void ?{}( Worker & this ) {
-	((thread&)this){ "Server Worker Thread", *options.the_cluster };
+	((thread&)this){ "Server Worker Thread", *options.clopts.instance };
 	int ret = pipe(this.pipe);
 	if( ret < 0 ) { abort( "pipe error: (%d) %s\n", (int)errno, strerror(errno) ); }
@@ -33,6 +33,9 @@
 void main( Worker & this ) {
 	CONNECTION:
-	while( int fd = take(wait_connect); fd >= 0) {
-            printf("New connection, waiting for requests\n");
+	for() {
+		int fd = take(wait_connect);
+		if (fd < 0) break;
+
+		printf("New connection %d, waiting for requests\n", fd);
 		REQUEST:
 		for() {
@@ -56,10 +59,10 @@
 			// If this wasn't a request retrun 400
 			if( code != OK200 ) {
-				printf("Invalid Request\n");
+				printf("Invalid Request : %d\n", code_val(code));
 				answer_error(fd, code);
 				continue REQUEST;
 			}
 
-			printf("Request for file %.*s\n", name_size, file);
+			printf("Request for file %.*s\n", (int)name_size, file);
 
 			// Get the fd from the file cache
@@ -90,5 +93,5 @@
 //=============================================================================================
 void ?{}( Acceptor & this, int sockfd, struct sockaddr * addr, socklen_t * addrlen, int flags ) {
-	((thread&)this){ "Acceptor Thread", *options.the_cluster };
+	((thread&)this){ "Acceptor Thread", *options.clopts.instance };
 	this.sockfd  = sockfd;
 	this.addr    = addr;
@@ -105,6 +108,6 @@
 		}
 
-            printf("New connection accepted\n");
+		printf("New connection accepted\n");
 		put( wait_connect, ret );
-      }
+	}
 }
