diff --git a/packages/common/index.js b/packages/common/index.js
index cb63e2ac3aeb88e0edbca2d85336e3c9389710d5..a562cd77d290c2caad3df20cdd295d377629429f 100644
--- a/packages/common/index.js
+++ b/packages/common/index.js
@@ -1,3 +1,4 @@
-import {EventBus} from './eventbus.js';
+import {EventBus} from './src/eventbus.js';
+import {createLinkedAbortController, createTimeoutAbortSignal} from './src/abort.js';
 
-export {EventBus};
\ No newline at end of file
+export {EventBus, createLinkedAbortController, createTimeoutAbortSignal};
\ No newline at end of file
diff --git a/packages/common/src/abort.js b/packages/common/src/abort.js
new file mode 100644
index 0000000000000000000000000000000000000000..33d5992812a478d05fd6d849f9ccbc51c73003a1
--- /dev/null
+++ b/packages/common/src/abort.js
@@ -0,0 +1,37 @@
+/**
+ * Takes multiple AbortSignal instances and returns a new AbortController which
+ * gets aborted if any of the AbortSignals do.
+ *
+ * @param  {...AbortSignal} signals
+ * @returns {AbortController}
+ */
+export function createLinkedAbortController(...signals) {
+    const controller = new AbortController();
+
+    for (const signal of signals) {
+        if (signal.aborted) {
+            controller.abort();
+            break;
+        } else {
+            signal.addEventListener('abort', () => {
+                controller.abort();
+            });
+        }
+    }
+
+    return controller;
+}
+
+/**
+ * Returns an AbortSignal which aborts after the specified time.
+ *
+ * @param {number} delay Delay in milliseconds
+ * @returns {AbortSignal}
+ */
+export function createTimeoutAbortSignal(delay) {
+    const controller = new AbortController();
+
+    setTimeout(() => {controller.abort(); }, delay);
+
+    return controller.signal;
+}
\ No newline at end of file
diff --git a/packages/common/eventbus.js b/packages/common/src/eventbus.js
similarity index 100%
rename from packages/common/eventbus.js
rename to packages/common/src/eventbus.js
diff --git a/packages/common/test/abort.js b/packages/common/test/abort.js
new file mode 100644
index 0000000000000000000000000000000000000000..9c0af4dc66a0f3cfdda95bd0b77fdc71bc1a3daf
--- /dev/null
+++ b/packages/common/test/abort.js
@@ -0,0 +1,20 @@
+import {assert} from 'chai';
+import {createLinkedAbortController, createTimeoutAbortSignal} from '../src/abort.js';
+
+suite('abort', () => {
+    test('createLinkedAbortController', () => {
+        let c1 = new AbortController();
+        let c2 = new AbortController();
+        const linked = createLinkedAbortController(c1.signal, c2.signal);
+        assert.isFalse(linked.signal.aborted);
+        c1.abort();
+        assert.isTrue(linked.signal.aborted);
+        c1.abort();
+        linked.abort();
+    });
+
+    test('createTimeoutAbortSignal', () => {
+        const signal = createTimeoutAbortSignal(10000000);
+        assert.isFalse(signal.aborted);
+    });
+});
\ No newline at end of file
diff --git a/packages/common/test/eventbus.js b/packages/common/test/eventbus.js
index cf6d94030ba693c511a77bb58b6bd573e5221bc2..209ff165ea4237cae4a440b9e2780620bee408df 100644
--- a/packages/common/test/eventbus.js
+++ b/packages/common/test/eventbus.js
@@ -1,5 +1,5 @@
 import {assert} from 'chai';
-import {EventBus, createEventName, checkIndentifier} from '../eventbus.js';
+import {EventBus, createEventName, checkIndentifier} from '../src/eventbus.js';
 
 suite('helpers', () => {
     test('createEventName', () => {