diff --git a/emper/io/Stats.cpp b/emper/io/Stats.cpp
index 17b7d8f32af71932014c9037752ea16353f5010e..21122bceca4ed048c65f2784d93ccd03d9c7b816 100644
--- a/emper/io/Stats.cpp
+++ b/emper/io/Stats.cpp
@@ -1,12 +1,16 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright © 2020-2021 Florian Fischer, Florian Schmaus
+// Copyright © 2020-2022 Florian Fischer, Florian Schmaus
 #include "io/Stats.hpp"
 
+#include <cerrno>
+#include <cstdlib>
 #include <initializer_list>	 // for initializer_list
 #include <iomanip>					 // for operator<<, setfill, setw
 #include <string>						 // for operator<<, to_string
 #include <utility>					 // for pair
 
+#include "Debug.hpp"
+#include "Future.hpp"
 #include "io/IoContext.hpp"
 #include "io/Operation.hpp"	 // for Operation, operator<<, Operation::RECV
 #include "lib/math.hpp"
@@ -119,6 +123,61 @@ auto operator<<(std::ostream& os, const Stats& s) -> std::ostream& {
 	return os;
 }
 
+void Stats::record_completion(Operation op, int32_t res, ssize_t partial_completion, size_t exp) {
+	auto& operation_map = io_uring_completions[op];
+
+	// Persistent Error
+	if (res < 0) {
+		if (partial_completion > 0) {
+			ATOMIC_INC_RELAXED(operation_map[IncrementalError]);
+		} else {
+			ATOMIC_INC_RELAXED(operation_map[ErrorCompletion]);
+		}
+		return;
+	}
+
+	// Make sure exp which is passed as future.len is only > 0 for Futures with a buffer
+	switch (op) {
+		case Operation::SEND:
+		case Operation::RECV:
+		case Operation::READ:
+		case Operation::WRITE:
+		case Operation::WRITEV:
+			break;
+		case Operation::TIMEOUT:
+			exp = ETIME;
+			break;
+		default:
+			exp = 0;
+			break;
+	}
+
+	// Full completion
+	if (!exp || (uint32_t)res == exp) {
+		ATOMIC_INC_RELAXED(operation_map[FullCompletion]);
+		return;
+	}
+
+	// we expect partial completion
+	if (partial_completion != PartialCompletableFuture::DISABLE_PARTIAL_COMPLETION) {
+		if ((size_t)(res + partial_completion) < exp) {
+			ATOMIC_INC_RELAXED(operation_map[PartialResubmission]);
+		} else {
+			ATOMIC_INC_RELAXED(operation_map[IncrementalCompletion]);
+		}
+		return;
+	}
+
+	if ((uint32_t)res < exp) {
+		ATOMIC_INC_RELAXED(operation_map[PartialCompletion]);
+		return;
+	}
+
+	LOGW("Unexpected completion case in IoStats");
+	LOGW(op << ", " << res << ", " << partial_completion << ", " << exp);
+	abort();
+}
+
 void Stats::printStats(IoContext* globalIoContext, const std::vector<IoContext*>& workerIoContexts,
 											 std::ostream& out) {
 	if (globalIoContext) {
diff --git a/emper/io/Stats.hpp b/emper/io/Stats.hpp
index cbb02ab9ff7de68a56875ebb3712bd0fd80ff3f6..c828a69bca0936d38399c655733a7aacceae7be3 100644
--- a/emper/io/Stats.hpp
+++ b/emper/io/Stats.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright © 2020-2021 Florian Fischer, Florian Schmaus
+// Copyright © 2020-2022 Florian Fischer, Florian Schmaus
 #pragma once
 #include <bits/types/struct_iovec.h>	// for iovec
 #include <sys/types.h>								// for ssize_t
@@ -8,13 +8,11 @@
 #include <chrono>		 // for nanoseconds
 #include <cstddef>	 // for size_t
 #include <cstdint>	 // for uint64_t, int32_t, uint32_t
-#include <cstdlib>	 // for abort
 #include <iostream>	 // for operator<<, basic_ostream, basi...
 #include <map>			 // for map, map<>::value_compare
 #include <vector>
 
 #include "CallerEnvironment.hpp"
-#include "Debug.hpp"				 // for LOGW
 #include "Emper.hpp"				 // for STATS
 #include "emper-common.h"		 // for workerid_t
 #include "io/Future.hpp"		 // for PartialCompletableFuture, Write...
@@ -156,57 +154,7 @@ class Stats {
 											exp);
 	}
 
-	void record_completion(Operation op, int32_t res, ssize_t partial_completion, size_t exp) {
-		auto& operation_map = io_uring_completions[op];
-
-		// Persistent Error
-		if (res < 0) {
-			if (partial_completion > 0) {
-				ATOMIC_INC_RELAXED(operation_map[IncrementalError]);
-			} else {
-				ATOMIC_INC_RELAXED(operation_map[ErrorCompletion]);
-			}
-			return;
-		}
-
-		// Make sure exp which is passed as future.len is only > 0 for Futures with a buffer
-		switch (op) {
-			case Operation::SEND:
-			case Operation::RECV:
-			case Operation::READ:
-			case Operation::WRITE:
-			case Operation::WRITEV:
-				break;
-			default:
-				exp = 0;
-				break;
-		}
-
-		// Full completion
-		if (!exp || (uint32_t)res == exp) {
-			ATOMIC_INC_RELAXED(operation_map[FullCompletion]);
-			return;
-		}
-
-		// we expect partial completion
-		if (partial_completion != PartialCompletableFuture::DISABLE_PARTIAL_COMPLETION) {
-			if ((size_t)(res + partial_completion) < exp) {
-				ATOMIC_INC_RELAXED(operation_map[PartialResubmission]);
-			} else {
-				ATOMIC_INC_RELAXED(operation_map[IncrementalCompletion]);
-			}
-			return;
-		}
-
-		if ((uint32_t)res < exp) {
-			ATOMIC_INC_RELAXED(operation_map[PartialCompletion]);
-			return;
-		}
-
-		LOGW("Unexpected completion case in IoStats");
-		LOGW(op << ", " << res << ", " << partial_completion << ", " << exp);
-		abort();
-	}
+	void record_completion(Operation op, int32_t res, ssize_t partial_completion, size_t exp);
 
 	// running mean calculation taken from
 	// https://math.stackexchange.com/questions/106700/incremental-averageing