Index: benchmark/io/http/http_ring.cpp
===================================================================
--- benchmark/io/http/http_ring.cpp	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
+++ benchmark/io/http/http_ring.cpp	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
@@ -0,0 +1,570 @@
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <iostream>
+
+#include <signal.h>
+#include <unistd.h>
+#include <liburing.h>
+
+typedef enum {
+	EVENT_END,
+	EVENT_ACCEPT,
+	EVENT_REQUEST,
+	EVENT_ANSWER
+} event_t;
+
+struct __attribute__((aligned(128))) request_t {
+	event_t type;
+	int fd;
+	size_t length;
+	char * buff;
+	char data[0];
+
+	static struct request_t * create(event_t type, size_t extra) {
+		auto ret = (struct request_t *)malloc(sizeof(struct request_t) + extra);
+		ret->type = type;
+		ret->length = extra;
+		ret->buff = ret->data;
+		return ret;
+	}
+
+	static struct request_t * create(event_t type) {
+		return create(type, 0);
+	}
+};
+
+struct __attribute__((aligned(128))) options_t {
+	struct {
+		int sockfd;
+		struct sockaddr *addr;
+		socklen_t *addrlen;
+		int flags;
+	} acpt;
+
+	int endfd;
+	struct io_uring * ring;
+
+	struct {
+		size_t subs = 0;
+		size_t cnts = 0;
+	} result;
+};
+
+//=========================================================
+static struct io_uring_sqe * get_sqe(struct io_uring * ring) {
+	struct io_uring_sqe * sqe = io_uring_get_sqe(ring);
+	if(!sqe) {
+		std::cerr << "Insufficient entries in ring" << std::endl;
+		exit(EXIT_FAILURE);
+	}
+	return sqe;
+}
+
+static void submit(struct io_uring * ) {
+	// io_uring_submit(ring);
+}
+
+//=========================================================
+static void ring_end(struct io_uring * ring, int fd, char * buffer, size_t len) {
+	struct io_uring_sqe * sqe = get_sqe(ring);
+	io_uring_prep_read(sqe, fd, buffer, len, 0);
+	io_uring_sqe_set_data(sqe, request_t::create(EVENT_END));
+	submit(ring);
+}
+
+static void ring_accept(struct io_uring * ring, int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
+	auto req = request_t::create(EVENT_ACCEPT);
+	struct io_uring_sqe * sqe = get_sqe(ring);
+	io_uring_prep_accept(sqe, sockfd, addr, addrlen, flags);
+	io_uring_sqe_set_data(sqe, req);
+	submit(ring);
+	// std::cout << "Submitted accept: " << req << std::endl;
+}
+
+static void ring_request(struct io_uring * ring, int fd) {
+	size_t size = 1024;
+	auto req = request_t::create(EVENT_REQUEST, size);
+	req->fd = fd;
+
+	struct io_uring_sqe * sqe = get_sqe(ring);
+	io_uring_prep_read(sqe, fd, req->buff, size, 0);
+	io_uring_sqe_set_data(sqe, req);
+	submit(ring);
+	// std::cout << "Submitted request: " << req << " (" << (void*)req->buffer << ")"<<std::endl;
+}
+
+//=========================================================
+enum HttpCode {
+	OK200 = 0,
+	E400,
+	E404,
+	E405,
+	E408,
+	E413,
+	E414,
+	KNOWN_CODES
+};
+
+const char * http_msgs[] = {
+	"HTTP/1.1 200 OK\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: %zu \n\n%s",
+	"HTTP/1.1 400 Bad Request\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
+	"HTTP/1.1 404 Not Found\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
+	"HTTP/1.1 405 Method Not Allowed\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
+	"HTTP/1.1 408 Request Timeout\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
+	"HTTP/1.1 413 Payload Too Large\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
+	"HTTP/1.1 414 URI Too Long\nServer: HttoForall\nDate: %s \nContent-Type: text/plain\nContent-Length: 0 \n\n",
+};
+
+static_assert( KNOWN_CODES == (sizeof(http_msgs ) / sizeof(http_msgs [0])));
+
+const int http_codes[] = {
+	200,
+	400,
+	404,
+	405,
+	408,
+	413,
+	414,
+};
+
+static_assert( KNOWN_CODES == (sizeof(http_codes) / sizeof(http_codes[0])));
+
+int code_val(HttpCode code) {
+	return http_codes[code];
+}
+
+static void ring_answer(struct io_uring * ring, int fd, HttpCode code) {
+	size_t size = 256;
+	auto req = request_t::create(EVENT_ANSWER, size);
+	req->fd = fd;
+
+	const char * fmt = http_msgs[code];
+	const char * date = "";
+	size = snprintf(req->buff, size, fmt, date, size);
+
+	struct io_uring_sqe * sqe = get_sqe(ring);
+	io_uring_prep_write(sqe, fd, req->buff, size, 0);
+	io_uring_sqe_set_data(sqe, req);
+	submit(ring);
+	// std::cout << "Submitted good answer: " << req << " (" << (void*)req->buffer << ")"<<std::endl;
+}
+
+static void ring_answer(struct io_uring * ring, int fd, const std::string &) {
+	// size_t size = 256;
+	// auto req = request_t::create(EVENT_ANSWER, size);
+	// req->fd = fd;
+
+	// const char * fmt = http_msgs[OK200];
+	// const char * date = "";
+	// size_t len = snprintf(req->buffer, size, fmt, date, ans.size(), ans.c_str());
+	// req->length = len;
+
+	// struct io_uring_sqe * sqe = get_sqe(ring);
+	// io_uring_prep_write(sqe, fd, req->buffer, len, 0);
+	// io_uring_sqe_set_data(sqe, req);
+	// submit(ring);
+	// std::cout << "Submitted good answer: " << req << " (" << (void*)req->buffer << ")"<<std::endl;
+
+
+	static const char* RESPONSE = "HTTP/1.1 200 OK\r\n" \
+						"Content-Length: 15\r\n" \
+						"Content-Type: text/html\r\n" \
+						"Connection: keep-alive\r\n" \
+						"Server: testserver\r\n" \
+						"\r\n" \
+						"Hello, World!\r\n";
+
+	static const size_t RLEN = strlen(RESPONSE);
+
+	size_t size = 256;
+	auto req = request_t::create(EVENT_ANSWER, size);
+	req->fd = fd;
+	req->buff = (char*)RESPONSE;
+	req->length = RLEN;
+
+	// const char * fmt = http_msgs[OK200];
+	// const char * date = "";
+	// size_t len = snprintf(req->buffer, size, fmt, date, ans.size(), ans.c_str());
+	// req->length = len;
+
+	struct io_uring_sqe * sqe = get_sqe(ring);
+	io_uring_prep_write(sqe, fd, RESPONSE, RLEN, 0);
+	io_uring_sqe_set_data(sqe, req);
+	submit(ring);
+}
+
+//=========================================================
+static void handle_new_conn(struct io_uring * ring, int fd) {
+	if( fd < 0 ) {
+		int err = -fd;
+		if( err == ECONNABORTED ) return;
+		std::cerr << "accept error: (" << errno << ") " << strerror(errno) << std::endl;
+		exit(EXIT_FAILURE);
+	}
+
+	ring_request(ring, fd);
+}
+
+static void handle_request(struct io_uring * ring, struct request_t * in, int res) {
+	if( res < 0 ) {
+		int err = -res;
+		switch(err) {
+			case EPIPE:
+			case ECONNRESET:
+				close(in->fd);
+				free(in);
+				return;
+			default:
+				std::cerr << "request error: (" << err << ") " << strerror(err) << std::endl;
+				exit(EXIT_FAILURE);
+		}
+	}
+
+	if(res == 0) {
+		close(in->fd);
+		free(in);
+		return;
+	}
+
+	const char * it = in->buff;
+	if( !strstr( it, "\r\n\r\n" ) ) {
+		std::cout << "Incomplete request" << std::endl;
+		close(in->fd);
+		free(in);
+		return;
+	}
+
+	it = in->buff;
+	const std::string reply = "Hello, World!\n";
+	int ret = memcmp(it, "GET ", 4);
+	if( ret != 0 ) {
+		ring_answer(ring, in->fd, E400);
+		goto NEXT;
+	}
+
+	it += 4;
+	ret = memcmp(it, "/plaintext", 10);
+	if( ret != 0 ) {
+		ring_answer(ring, in->fd, E404);
+		goto NEXT;
+	}
+
+	ring_answer(ring, in->fd, reply);
+
+	NEXT:
+		ring_request(ring, in->fd);
+		return;
+}
+
+static void handle_answer(struct io_uring * ring, struct request_t * in, int res) {
+	if( res < 0 ) {
+		int err = -res;
+		switch(err) {
+			case EPIPE:
+			case ECONNRESET:
+				close(in->fd);
+				free(in);
+				return;
+			default:
+				std::cerr << "answer error: (" << err << ") " << strerror(err) << std::endl;
+				exit(EXIT_FAILURE);
+		}
+	}
+
+	if( res >= in->length ) {
+		free(in);
+		return;
+	}
+
+	struct io_uring_sqe * sqe = get_sqe(ring);
+	io_uring_prep_write(sqe, in->fd, in->buff + res, in->length - res, 0);
+	io_uring_sqe_set_data(sqe, in);
+	submit(ring);
+	// std::cout << "Re-Submitted request: " << in << " (" << (void*)in->buffer << ")"<<std::endl;
+
+	ring_request(ring, in->fd);
+}
+
+//=========================================================
+extern "C" {
+extern int __io_uring_flush_sq(struct io_uring *ring);
+}
+
+void * proc_loop(void * arg) {
+	size_t count = 0;
+	struct options_t & opt = *(struct options_t *)arg;
+
+	struct io_uring * ring = opt.ring;
+
+	char endfd_buf[8];
+	ring_end(ring, opt.endfd, endfd_buf, 8);
+
+	ring_accept(ring, opt.acpt.sockfd, opt.acpt.addr, opt.acpt.addrlen, opt.acpt.flags);
+
+	bool done = false;
+	while(!done) {
+    		struct io_uring_cqe *cqe;
+		int ret;
+		while(-EAGAIN == (ret = io_uring_wait_cqe_nr(ring, &cqe, 0))) {
+			ret = io_uring_submit_and_wait(ring, 1);
+			if (ret < 0) {
+				fprintf( stderr, "io_uring get error: (%d) %s\n", (int)-ret, strerror(-ret) );
+				exit(EXIT_FAILURE);
+			}
+			opt.result.subs += ret;
+			opt.result.cnts++;
+		}
+
+		if (ret < 0 && -EAGAIN != ret) {
+			fprintf( stderr, "io_uring peek error: (%d) %s\n", (int)-ret, strerror(-ret) );
+			exit(EXIT_FAILURE);
+		}
+
+		auto req = (struct request_t *)cqe->user_data;
+		// std::cout << req << " completed with " << cqe->res << std::endl;
+
+		switch(req->type) {
+			case EVENT_END:
+				done = true;
+				break;
+			case EVENT_ACCEPT:
+				handle_new_conn(ring, cqe->res);
+				free(req);
+				ring_accept(ring, opt.acpt.sockfd, opt.acpt.addr, opt.acpt.addrlen, opt.acpt.flags);
+				break;
+			case EVENT_REQUEST:
+				handle_request(ring, req, cqe->res);
+				break;
+			case EVENT_ANSWER:
+				handle_answer(ring, req, cqe->res);
+				break;
+		}
+
+		io_uring_cqe_seen(ring, cqe);
+	}
+
+	return (void*)count;
+}
+
+//=========================================================
+struct __attribute__((aligned(128))) aligned_ring {
+	struct io_uring storage;
+};
+
+#include <bit>
+
+#include <pthread.h>
+extern "C" {
+	#include <signal.h>
+	#include <sys/eventfd.h>
+	#include <sys/socket.h>
+	#include <netinet/in.h>
+}
+
+int main(int argc, char * argv[]) {
+	signal(SIGPIPE, SIG_IGN);
+
+	unsigned nthreads = 1;
+	unsigned port = 8800;
+	unsigned entries = 256;
+	unsigned backlog = 10;
+	bool attach = false;
+
+	//===================
+	// Arguments
+	int c;
+	while ((c = getopt (argc, argv, "t:p:e:b:a")) != -1) {
+		switch (c)
+		{
+		case 't':
+			nthreads = atoi(optarg);
+			break;
+		case 'p':
+			port = atoi(optarg);
+			break;
+		case 'e':
+			entries = atoi(optarg);
+			break;
+		case 'b':
+			backlog = atoi(optarg);
+			break;
+		case 'a':
+			attach = true;
+			break;
+		case '?':
+		default:
+			std::cerr << "Usage: -t <threads> -p <port> -e <entries> -b <backlog> -a" << std::endl;
+			return EXIT_FAILURE;
+		}
+	}
+
+	if( !std::ispow2(entries) ) {
+		unsigned v = entries;
+		v--;
+		v |= v >> 1;
+		v |= v >> 2;
+		v |= v >> 4;
+		v |= v >> 8;
+		v |= v >> 16;
+		v++;
+		std::cerr << "Warning: num_entries not a power of 2 (" << entries << ") raising to " << v << std::endl;
+		entries = v;
+	}
+
+	//===================
+	// End FD
+	int efd = eventfd(0, EFD_SEMAPHORE);
+	if (efd < 0) {
+		std::cerr << "eventfd error: (" << errno << ") " << strerror(errno) << std::endl;
+		exit(EXIT_FAILURE);
+	}
+
+	//===================
+	// Open Socket
+	std::cout << getpid() << " : Listening on port " << port << std::endl;
+	int server_fd = socket(AF_INET, SOCK_STREAM, 0);
+	if(server_fd < 0) {
+		std::cerr << "socket error: (" << errno << ") " << strerror(errno) << std::endl;
+		exit(EXIT_FAILURE);
+	}
+
+	int ret = 0;
+	struct sockaddr_in address;
+	int addrlen = sizeof(address);
+	memset( (char *)&address, '\0', addrlen );
+	address.sin_family = AF_INET;
+	address.sin_addr.s_addr = htonl(INADDR_ANY);
+	address.sin_port = htons( port );
+
+	int waited = 0;
+	while(true) {
+		ret = bind( server_fd, (struct sockaddr *)&address, sizeof(address) );
+		if(ret < 0) {
+			if(errno == EADDRINUSE) {
+				if(waited == 0) {
+					std::cerr << "Waiting for port" << std::endl;
+				} else {
+					std::cerr << "\r" << waited;
+					std::cerr.flush();
+				}
+				waited ++;
+				usleep( 1000000 );
+				continue;
+			}
+			std::cerr << "bind error: (" << errno << ") " << strerror(errno) << std::endl;
+			exit(EXIT_FAILURE);
+		}
+		break;
+	}
+
+	ret = listen( server_fd, backlog );
+	if(ret < 0) {
+		std::cerr << "listen error: (" << errno << ") " << strerror(errno) << std::endl;
+		exit(EXIT_FAILURE);
+	}
+
+	//===================
+	// Run Server Threads
+	std::cout << "Starting " << nthreads << " Threads";
+	if(attach) {
+		std::cout << " with attached Rings";
+	}
+	std::cout << std::endl;
+
+	aligned_ring thrd_rings[nthreads];
+	pthread_t    thrd_hdls[nthreads];
+	options_t    thrd_opts[nthreads];
+	for(unsigned i = 0; i < nthreads; i++) {
+		if(!attach || i == 0) {
+			io_uring_queue_init(entries, &thrd_rings[i].storage, 0);
+		}
+		else {
+			struct io_uring_params p;
+			memset(&p, 0, sizeof(p));
+			p.flags = IORING_SETUP_ATTACH_WQ;
+			p.wq_fd = thrd_rings[0].storage.ring_fd;
+			io_uring_queue_init_params(entries, &thrd_rings[i].storage, &p);
+		}
+
+		thrd_opts[i].acpt.sockfd  = server_fd;
+		thrd_opts[i].acpt.addr    = (struct sockaddr *)&address;
+		thrd_opts[i].acpt.addrlen = (socklen_t*)&addrlen;
+		thrd_opts[i].acpt.flags   = 0;
+		thrd_opts[i].endfd        = efd;
+		thrd_opts[i].ring         = &thrd_rings[i].storage;
+
+		int ret = pthread_create(&thrd_hdls[i], nullptr, proc_loop, &thrd_opts[i]);
+		if (ret < 0) {
+			std::cerr << "pthread create error: (" << errno << ") " << strerror(errno) << std::endl;
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	//===================
+	// Server Started
+	std::cout << "Server Started" << std::endl;
+	{
+		char buffer[128];
+		int ret;
+		do {
+			ret = read(STDIN_FILENO, buffer, 128);
+			if(ret < 0) {
+				std::cerr << "main read error: (" << errno << ") " << strerror(errno) << std::endl;
+				exit(EXIT_FAILURE);
+			}
+			else if(ret > 0) {
+				std::cout << "User inputed '";
+				std::cout.write(buffer, ret);
+				std::cout << "'" << std::endl;
+			}
+		} while(ret != 0);
+
+		std::cout << "Shutdown received" << std::endl;
+	}
+
+	//===================
+	(std::cout << "Sending Shutdown to Threads... ").flush();
+	ret = eventfd_write(efd, nthreads);
+	if (ret < 0) {
+		std::cerr << "eventfd close error: (" << errno << ") " << strerror(errno) << std::endl;
+		exit(EXIT_FAILURE);
+	}
+	std::cout << "done" << std::endl;
+
+	//===================
+	(std::cout << "Stopping Threads Done... ").flush();
+	size_t total = 0;
+	size_t count = 0;
+	for(unsigned i = 0; i < nthreads; i++) {
+		void * retval;
+		int ret = pthread_join(thrd_hdls[i], &retval);
+		if (ret < 0) {
+			std::cerr << "pthread create error: (" << errno << ") " << strerror(errno) << std::endl;
+			exit(EXIT_FAILURE);
+		}
+		// total += (size_t)retval;
+		total += thrd_opts[i].result.subs;
+		count += thrd_opts[i].result.cnts;
+
+		io_uring_queue_exit(thrd_opts[i].ring);
+	}
+	std::cout << "done" << std::endl;
+	std::cout << "Submit average: " << total << "/" << count << "(" << (((double)total) / count) << ")" << std::endl;
+
+	//===================
+	(std::cout << "Closing Socket... ").flush();
+	ret = shutdown( server_fd, SHUT_RD );
+	if( ret < 0 ) {
+		std::cerr << "shutdown socket error: (" << errno << ") " << strerror(errno) << std::endl;
+		exit(EXIT_FAILURE);
+	}
+
+	ret = close(server_fd);
+	if (ret < 0) {
+		std::cerr << "close socket error: (" << errno << ") " << strerror(errno) << std::endl;
+		exit(EXIT_FAILURE);
+	}
+	std::cout << "done" << std::endl;
+}
Index: benchmark/io/http/main.cfa
===================================================================
--- benchmark/io/http/main.cfa	(revision 1adab3e7b648d40ce0b456c60f502607c6016d90)
+++ benchmark/io/http/main.cfa	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
@@ -6,4 +6,5 @@
 #include <unistd.h>
 extern "C" {
+	#include <signal.h>
 	#include <sys/socket.h>
 	#include <netinet/in.h>
@@ -78,4 +79,7 @@
 //============================================================================================='
 int main( int argc, char * argv[] ) {
+	__sighandler_t s = 1p;
+	signal(SIGPIPE, s);
+
 	//===================
 	// Parse args
Index: benchmark/io/http/options.cfa
===================================================================
--- benchmark/io/http/options.cfa	(revision 1adab3e7b648d40ce0b456c60f502607c6016d90)
+++ benchmark/io/http/options.cfa	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
@@ -9,4 +9,5 @@
 }
 
+#include <bitmanip.hfa>
 #include <fstream.hfa>
 #include <kernel.hfa>
@@ -51,4 +52,6 @@
 	bool iokpoll = false;
 	unsigned sublen = 16;
+	unsigned nentries = 16;
+
 
 	static cfa_option opt[] = {
@@ -68,4 +71,5 @@
 		{ 'i', "kpollcomplete",  "If set, cluster uses IORING_SETUP_IOPOLL", iokpoll, parse_settrue },
 		{'\0', "submitlength",   "Max number of submitions that can be submitted together", sublen },
+		{'\0', "numentries",     "Number of I/O entries", nentries },
 
 	};
@@ -74,4 +78,18 @@
 	char **left;
 	parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]... [PATH]\ncforall http server", left );
+
+	if( !is_pow2(nentries) ) {
+		unsigned v = nentries;
+		v--;
+		v |= v >> 1;
+		v |= v >> 2;
+		v |= v >> 4;
+		v |= v >> 8;
+		v |= v >> 16;
+		v++;
+		serr | "Warning: num_entries not a power of 2" | '(' | nentries | ')' | "raising to " | v;
+		nentries = v;
+	}
+	options.clopts.params.num_entries = nentries;
 
 	options.clopts.params.poller_submits = subthrd;
@@ -94,5 +112,5 @@
 	options.clopts.params.num_ready = sublen;
 
-	if( left[0] == 0p ) { return "."; }
+	if( left[0] == 0p ) { return; }
 
 	const char * path = left[0];
@@ -100,5 +118,9 @@
 
 	if( left[0] != 0p ) {
-		serr | "Too many trailing arguments!";
+		serr | "Too many trailing arguments!" | '\'' | path | '\'';
+		while(left[0] != 0p) {
+			serr | " - " | left[0];
+			left++;
+		}
 		exit(EXIT_FAILURE);
 	}
Index: benchmark/io/setup.sh
===================================================================
--- benchmark/io/setup.sh	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
+++ benchmark/io/setup.sh	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
@@ -0,0 +1,33 @@
+#!/bin/bash
+[ $(id -u) -ne 0 ] && exec sudo $0 $*
+
+#========================================
+echo "Sysctl Calls"
+sysctl -w net.core.somaxconn=1048576
+sysctl -w net.ipv4.tcp_max_syn_backlog=262144
+sysctl -w net.ipv4.tcp_tw_reuse=1
+sysctl -w net.ipv4.ip_local_port_range="1024 65535"
+sysctl -w vm.max_map_count=2097152
+cpupower frequency-set -g performance
+
+# sysctl net.core.somaxconn
+# sysctl net.ipv4.tcp_max_syn_backlog
+# sysctl net.ipv4.tcp_tw_reuse
+# sysctl net.ipv4.ip_local_port_range
+# sysctl vm.max_map_count
+
+#========================================
+echo "Check what we can debug/trace"
+
+echo "/proc/sys/kernel/yama/ptrace_scope"
+cat /proc/sys/kernel/yama/ptrace_scope
+echo "/proc/sys/kernel/kptr_restrict"
+cat /proc/sys/kernel/kptr_restrict
+echo "/proc/sys/kernel/nmi_watchdog"
+cat /proc/sys/kernel/nmi_watchdog
+echo "/proc/sys/kernel/perf_event_paranoid"
+cat /proc/sys/kernel/perf_event_paranoid
+
+#========================================
+echo "User Limits"
+ulimit -a
Index: example/io/attach-block.c
===================================================================
--- example/io/attach-block.c	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
+++ example/io/attach-block.c	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
@@ -0,0 +1,108 @@
+/*
+This is an example that uses io_uring with attach mode.
+It demonstrates the what happens if some thread submits to
+the original ring and then spins, while a second thread waits
+on the attached ring. This deadlocks because even if rings are
+attached, they don't share a completion queue. Therefore all
+completion queues must be polled for progress to be made.
+
+It uses liburing for simplicity.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <pthread.h>
+#include <liburing.h>
+
+volatile bool done = false;
+
+void * submitter(void * arg) {
+	struct io_uring * ring = (struct io_uring *)arg;
+
+	const char * path = __FILE__;
+
+	int fd = open(path, 0, O_RDONLY);
+	if( fd < 0) {
+		fprintf(stderr, "Can't open file: (%d) %s\n", errno, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+	printf("Opened file: %d\n", fd);
+
+	char buffer[1024];
+	struct io_uring_sqe * sqe = io_uring_get_sqe(ring);
+	io_uring_prep_read(sqe, fd, buffer, 1024, 0);
+	io_uring_submit(ring);
+
+	printf("Submitted read\n");
+
+	printf("Spinning\n");
+	while(!__atomic_load_n(&done, __ATOMIC_SEQ_CST));
+
+	return NULL;
+}
+
+void * poller(void * arg) {
+	struct io_uring * ring = (struct io_uring *)arg;
+	struct io_uring_cqe *cqe;
+
+	printf("Waiting for results\n");
+
+	int ret = io_uring_wait_cqe(ring, &cqe);
+	if( ret < 0) {
+		fprintf(stderr, "ioring wait failed: (%d) %s\n", -ret, strerror(-ret));
+		exit(EXIT_FAILURE);
+	}
+	io_uring_cqe_seen(ring, cqe);
+
+	printf("Got result\n");
+
+	__atomic_store_n(&done, true, __ATOMIC_SEQ_CST);
+	return NULL;
+}
+
+int main() {
+	struct io_uring base_ring;
+	io_uring_queue_init(8, &base_ring, 0);
+	printf("Created first ring: %d\n", base_ring.ring_fd);
+
+	struct io_uring attached_ring;
+	struct io_uring_params par;
+	memset(&par, '\0', sizeof(struct io_uring_params));
+	par.flags |= IORING_SETUP_ATTACH_WQ;
+	par.wq_fd = base_ring.ring_fd;
+	io_uring_queue_init_params(8, &attached_ring, &par);
+	printf("Attached second ring: %d\n", attached_ring.ring_fd);
+
+	printf("Forking\n");
+	pthread_t s, p;
+
+	int ret = pthread_create(&s, NULL, submitter, &base_ring);
+	if( ret < 0) {
+		fprintf(stderr, "pthread create 1 error: (%d) %s\n", errno, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	ret = pthread_create(&p, NULL, poller, &attached_ring);
+	if( ret < 0) {
+		fprintf(stderr, "pthread create 2 error: (%d) %s\n", errno, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	ret = pthread_join(p, NULL);
+	if( ret < 0) {
+		fprintf(stderr, "pthread join 2 error: (%d) %s\n", errno, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	ret = pthread_join(s, NULL);
+	if( ret < 0) {
+		fprintf(stderr, "pthread join 1 error: (%d) %s\n", errno, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+}
Index: example/io/attach-end.c
===================================================================
--- example/io/attach-end.c	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
+++ example/io/attach-end.c	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
@@ -0,0 +1,106 @@
+/*
+This is an example that uses io_uring with attach mode.
+It demonstrates the what happens if the original ring is deleted before the other.
+It uses liburing for simplicity.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <liburing.h>
+
+
+char buffer1[1024];
+char buffer2[1024];
+
+static void test_read(struct io_uring * ring, int fd) {
+	printf("Testing ring %d\n", ring->ring_fd);
+	struct io_uring_sqe * sqe = io_uring_get_sqe(ring);
+	io_uring_prep_read(sqe, fd, buffer2, 1024, 0);
+
+	io_uring_submit(ring);
+
+	struct io_uring_cqe *cqe;
+	int ret = io_uring_wait_cqe(ring, &cqe);
+	if( ret < 0) {
+		fprintf(stderr, "ioring wait failed: (%d) %s\n", -ret, strerror(-ret));
+		exit(EXIT_FAILURE);
+	}
+	io_uring_cqe_seen(ring, cqe);
+
+	ret = memcmp(buffer1, buffer2, 1024);
+	if(ret != 0) {
+		fprintf(stderr, "files are different!\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+int main() {
+	const char * path = __FILE__;
+
+	int fd = open(path, 0, O_RDONLY);
+	if( fd < 0) {
+		fprintf(stderr, "Can't open file: (%d) %s\n", errno, strerror(errno));
+		return 1;
+	}
+
+	int ret = read(fd, buffer1, 1024);
+	if( ret < 0 ) {
+		fprintf(stderr, "Can't read file: (%d) %s\n", errno, strerror(errno));
+		return 1;
+	}
+
+	struct io_uring first_ring;
+	io_uring_queue_init(8, &first_ring, 0);
+	printf("Created first ring: %d\n", first_ring.ring_fd);
+
+	test_read(&first_ring, fd);
+
+
+	struct io_uring second_ring;
+	struct io_uring_params p;
+	memset(&p, '\0', sizeof(struct io_uring_params));
+	p.flags |= IORING_SETUP_ATTACH_WQ;
+	p.wq_fd = first_ring.ring_fd;
+	io_uring_queue_init_params(8, &second_ring, &p);
+	printf("Attached second ring: %d\n", second_ring.ring_fd);
+
+	test_read(&second_ring, fd);
+
+	printf("Deleting first ring: %d\n", first_ring.ring_fd);
+	io_uring_queue_exit(&first_ring);
+
+	printf("Sleeping (for good measure)\n");
+
+	usleep( 1000000 );
+
+	test_read(&second_ring, fd);
+
+	struct io_uring third_ring;
+	memset(&p, '\0', sizeof(struct io_uring_params));
+	p.flags |= IORING_SETUP_ATTACH_WQ;
+	p.wq_fd = second_ring.ring_fd;
+	io_uring_queue_init_params(8, &third_ring, &p);
+	printf("Attached third ring: %d\n", third_ring.ring_fd);
+
+	test_read(&third_ring, fd);
+
+	printf("Deleting second ring: %d\n", second_ring.ring_fd);
+	io_uring_queue_exit(&second_ring);
+
+	printf("Sleeping (for good measure)\n");
+
+	usleep( 1000000 );
+
+	io_uring_queue_exit(&third_ring);
+
+	close(fd);
+
+	return 0;
+
+}
Index: libcfa/src/concurrency/io.cfa
===================================================================
--- libcfa/src/concurrency/io.cfa	(revision 1adab3e7b648d40ce0b456c60f502607c6016d90)
+++ libcfa/src/concurrency/io.cfa	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
@@ -41,5 +41,5 @@
 	#include "io/types.hfa"
 
-	static const char * opcodes[] = {
+	__attribute__((unused)) static const char * opcodes[] = {
 		"OP_NOP",
 		"OP_READV",
@@ -173,8 +173,11 @@
 			__cfadbg_print_safe(io_core, "Kernel I/O : IO_URING enter %d %u %u\n", ring.fd, to_submit, flags);
 			ret = syscall( __NR_io_uring_enter, ring.fd, to_submit, 0, flags, (sigset_t *)0p, _NSIG / 8);
+			__cfadbg_print_safe(io_core, "Kernel I/O : IO_URING %d returned %d\n", ring.fd, ret);
+
 			if( ret < 0 ) {
 				switch((int)errno) {
 				case EAGAIN:
 				case EINTR:
+				case EBUSY:
 					ret = -1;
 					break;
@@ -465,6 +468,6 @@
 				sqe->flags,
 				sqe->ioprio,
-				sqe->off,
-				sqe->addr,
+				(void*)sqe->off,
+				(void*)sqe->addr,
 				sqe->len,
 				sqe->accept_flags,
@@ -491,5 +494,5 @@
 		}
 		else if( ring.eager_submits ) {
-			__u32 picked = __submit_to_ready_array( ring, idx, mask );
+			__attribute__((unused)) __u32 picked = __submit_to_ready_array( ring, idx, mask );
 
 			#if defined(LEADER_LOCK)
@@ -629,6 +632,6 @@
 					sqe->flags,
 					sqe->ioprio,
-					sqe->off,
-					sqe->addr,
+					(void*)sqe->off,
+					(void*)sqe->addr,
 					sqe->len,
 					sqe->accept_flags,
@@ -642,4 +645,5 @@
 			__atomic_thread_fence( __ATOMIC_SEQ_CST );
 			// Release the consumed SQEs
+
 			__release_consumed_submission( ring );
 			// ring.submit_q.sqes[idx].user_data = 3ul64;
Index: libcfa/src/concurrency/io/setup.cfa
===================================================================
--- libcfa/src/concurrency/io/setup.cfa	(revision 1adab3e7b648d40ce0b456c60f502607c6016d90)
+++ libcfa/src/concurrency/io/setup.cfa	(revision 7b91c0e992ed493cc46d297ec3fe313c381a8dbc)
@@ -549,5 +549,5 @@
 		int ret = syscall( __NR_io_uring_register, ctx.thrd.ring->fd, IORING_REGISTER_FILES, files, count );
 		if( ret < 0 ) {
-			abort( "KERNEL ERROR: IO_URING SYSCALL - (%d) %s\n", (int)errno, strerror(errno) );
+			abort( "KERNEL ERROR: IO_URING REGISTER - (%d) %s\n", (int)errno, strerror(errno) );
 		}
 
