Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 2ca5a198f7308b0c892b04c0fcda44f5e83eb8f0)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 73a5857016948b0a6daad123e7cef8b6e4a8ebe7)
@@ -277,16 +277,11 @@
 		goto err_cmd;
 
-	fid_t fid = fibril_create(&event_worker, hc);
-	if (!fid)
+	hc->event_worker = joinable_fibril_create(&event_worker, hc);
+	if (!hc->event_worker)
 		goto err_bus;
 
-	// TODO: completion_reset
-	hc->event_fibril_completion.active = true;
-	fibril_mutex_initialize(&hc->event_fibril_completion.guard);
-	fibril_condvar_initialize(&hc->event_fibril_completion.cv);
-
 	xhci_sw_ring_init(&hc->sw_ring, PAGE_SIZE / sizeof(xhci_trb_t));
 
-	fibril_add_ready(fid);
+	joinable_fibril_start(hc->event_worker);
 
 	return EOK;
@@ -598,11 +593,5 @@
 	}
 
-	// TODO: completion_complete
-	fibril_mutex_lock(&hc->event_fibril_completion.guard);
-	hc->event_fibril_completion.active = false;
-	fibril_condvar_broadcast(&hc->event_fibril_completion.cv);
-	fibril_mutex_unlock(&hc->event_fibril_completion.guard);
-
-	return EOK;
+	return 0;
 }
 
@@ -688,11 +677,5 @@
 {
 	xhci_sw_ring_stop(&hc->sw_ring);
-
-	// TODO: completion_wait
-	fibril_mutex_lock(&hc->event_fibril_completion.guard);
-	while (hc->event_fibril_completion.active)
-		fibril_condvar_wait(&hc->event_fibril_completion.cv,
-		    &hc->event_fibril_completion.guard);
-	fibril_mutex_unlock(&hc->event_fibril_completion.guard);
+	joinable_fibril_join(hc->event_worker);
 	xhci_sw_ring_fini(&hc->sw_ring);
 
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 2ca5a198f7308b0c892b04c0fcda44f5e83eb8f0)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 73a5857016948b0a6daad123e7cef8b6e4a8ebe7)
@@ -39,4 +39,5 @@
 #include <fibril_synch.h>
 #include <usb/host/usb_transfer_batch.h>
+#include <usb/host/utility.h>
 #include "hw_struct/regs.h"
 #include "hw_struct/context.h"
@@ -78,9 +79,6 @@
 	xhci_sw_ring_t sw_ring;
 
-	struct {
-		fibril_mutex_t guard;
-		fibril_condvar_t cv;
-		bool active;
-	} event_fibril_completion;
+	/** Event handling fibril */
+	joinable_fibril_t *event_worker;
 
 	/* Root hub emulation */
Index: uspace/drv/bus/usb/xhci/rh.c
===================================================================
--- uspace/drv/bus/usb/xhci/rh.c	(revision 2ca5a198f7308b0c892b04c0fcda44f5e83eb8f0)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision 73a5857016948b0a6daad123e7cef8b6e4a8ebe7)
@@ -96,6 +96,6 @@
 	}
 
-	fid_t fid = fibril_create(&rh_worker, rh);
-	if (!fid) {
+	rh->event_worker = joinable_fibril_create(&rh_worker, rh);
+	if (!rh->event_worker) {
 		free(rh->ports);
 		return err;
@@ -113,9 +113,5 @@
 	xhci_sw_ring_init(&rh->event_ring, rh->max_ports);
 
-	hc->event_fibril_completion.active = true;
-	fibril_mutex_initialize(&hc->event_fibril_completion.guard);
-	fibril_condvar_initialize(&hc->event_fibril_completion.cv);
-
-	fibril_add_ready(fid);
+	joinable_fibril_start(rh->event_worker);
 
 	return EOK;
@@ -132,11 +128,5 @@
 
 	xhci_sw_ring_stop(&rh->event_ring);
-
-	// TODO: completion_wait
-	fibril_mutex_lock(&rh->event_fibril_completion.guard);
-	while (rh->event_fibril_completion.active)
-		fibril_condvar_wait(&rh->event_fibril_completion.cv,
-		    &rh->event_fibril_completion.guard);
-	fibril_mutex_unlock(&rh->event_fibril_completion.guard);
+	joinable_fibril_join(rh->event_worker);
 	xhci_sw_ring_fini(&rh->event_ring);
 	return EOK;
@@ -332,11 +322,5 @@
 	}
 
-	// TODO: completion_complete
-	fibril_mutex_lock(&rh->event_fibril_completion.guard);
-	rh->event_fibril_completion.active = false;
-	fibril_condvar_broadcast(&rh->event_fibril_completion.cv);
-	fibril_mutex_unlock(&rh->event_fibril_completion.guard);
-
-	return EOK;
+	return 0;
 }
 
Index: uspace/drv/bus/usb/xhci/rh.h
===================================================================
--- uspace/drv/bus/usb/xhci/rh.h	(revision 2ca5a198f7308b0c892b04c0fcda44f5e83eb8f0)
+++ uspace/drv/bus/usb/xhci/rh.h	(revision 73a5857016948b0a6daad123e7cef8b6e4a8ebe7)
@@ -37,6 +37,7 @@
 #define XHCI_RH_H
 
+#include <usb/host/bus.h>
 #include <usb/host/usb_transfer_batch.h>
-#include <usb/host/bus.h>
+#include <usb/host/utility.h>
 
 #include "hw_struct/regs.h"
@@ -77,9 +78,5 @@
 	xhci_sw_ring_t event_ring;
 
-	struct {
-		fibril_mutex_t guard;
-		fibril_condvar_t cv;
-		bool active;
-	} event_fibril_completion;
+	joinable_fibril_t *event_worker;
 } xhci_rh_t;
 
Index: uspace/lib/usbhost/include/usb/host/utility.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/utility.h	(revision 2ca5a198f7308b0c892b04c0fcda44f5e83eb8f0)
+++ uspace/lib/usbhost/include/usb/host/utility.h	(revision 73a5857016948b0a6daad123e7cef8b6e4a8ebe7)
@@ -54,4 +54,15 @@
 int hc_device_explore(device_t *);
 
+/** Joinable fibril */
+
+typedef int (*fibril_worker_t)(void *);
+typedef struct joinable_fibril joinable_fibril_t;
+
+joinable_fibril_t *joinable_fibril_create(fibril_worker_t, void *);
+void joinable_fibril_start(joinable_fibril_t *);
+void joinable_fibril_join(joinable_fibril_t *);
+void joinable_fibril_destroy(joinable_fibril_t *);
+
+
 #endif
 /**
Index: uspace/lib/usbhost/src/utility.c
===================================================================
--- uspace/lib/usbhost/src/utility.c	(revision 2ca5a198f7308b0c892b04c0fcda44f5e83eb8f0)
+++ uspace/lib/usbhost/src/utility.c	(revision 73a5857016948b0a6daad123e7cef8b6e4a8ebe7)
@@ -284,4 +284,86 @@
 }
 
+typedef struct joinable_fibril {
+	fid_t fid;
+	void *arg;
+	fibril_worker_t worker;
+
+	bool running;
+	fibril_mutex_t guard;
+	fibril_condvar_t dead_cv;
+} joinable_fibril_t;
+
+static int joinable_fibril_worker(void *arg)
+{
+	joinable_fibril_t *jf = arg;
+	jf->worker(jf->arg);
+
+	fibril_mutex_lock(&jf->guard);
+	jf->running = false;
+	fibril_mutex_unlock(&jf->guard);
+	fibril_condvar_broadcast(&jf->dead_cv);
+	return 0;
+}
+
+/**
+ * Create a fibril that is joinable. Similar to fibril_create.
+ */
+joinable_fibril_t *joinable_fibril_create(fibril_worker_t worker, void *arg)
+{
+	joinable_fibril_t *jf = calloc(1, sizeof(joinable_fibril_t));
+	if (!jf)
+		return NULL;
+
+	jf->fid = fibril_create(joinable_fibril_worker, jf);
+	if (!jf->fid) {
+		free(jf);
+		return NULL;
+	}
+
+	jf->worker = worker;
+	jf->arg = arg;
+	fibril_mutex_initialize(&jf->guard);
+	fibril_condvar_initialize(&jf->dead_cv);
+
+	return jf;
+}
+
+
+/**
+ * Start a joinable fibril. Similar to fibril_add_ready.
+ */
+void joinable_fibril_start(joinable_fibril_t *jf)
+{
+	assert(jf);
+	assert(!jf->running);
+
+	jf->running = true;
+	fibril_add_ready(jf->fid);
+}
+
+/**
+ * Join a joinable fibril. Not similar to anything, obviously.
+ */
+void joinable_fibril_join(joinable_fibril_t *jf)
+{
+	assert(jf);
+
+	fibril_mutex_lock(&jf->guard);
+	while (jf->running)
+		fibril_condvar_wait(&jf->dead_cv, &jf->guard);
+	fibril_mutex_unlock(&jf->guard);
+}
+
+/**
+ * Regular fibrils clean after themselves, joinable fibrils cannot.
+ */
+void joinable_fibril_destroy(joinable_fibril_t *jf)
+{
+	if (jf) {
+		joinable_fibril_join(jf);
+		free(jf);
+	}
+}
+
 /**
  * @}
