diff --git a/include/libcamera/internal/mailbox.h b/include/libcamera/internal/mailbox.h
new file mode 100644
index 00000000..fb27f5bb
--- /dev/null
+++ b/include/libcamera/internal/mailbox.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * mailbox.h - Template class for generic mailbox
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace libcamera {
+
+template<class T>
+class MailBox
+{
+public:
+	using Recycler = std::function<void(T &)>;
+
+	MailBox()
+		: valid_(false) {}
+	~MailBox();
+
+	void put(const T &item, Recycler recycler);
+
+	const T &get();
+
+	bool valid() { return valid_; }
+
+private:
+	T item_;
+	bool valid_;
+	std::function<void(T &)> recycler_;
+};
+
+template<class T>
+using SharedMailBox = std::shared_ptr<MailBox<T>>;
+
+template<class T>
+SharedMailBox<T> makeMailBox()
+{
+	return std::make_shared<MailBox<T>>();
+}
+
+template<class T>
+std::vector<SharedMailBox<T>> makeMailBoxVector(const unsigned int count);
+
+} /* namespace libcamera */
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index 1c5eef9c..4c1228b9 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -27,6 +27,7 @@ libcamera_internal_headers = files([
     'ipa_proxy.h',
     'ipc_pipe.h',
     'ipc_unixsocket.h',
+    'mailbox.h',
     'mapped_framebuffer.h',
     'media_device.h',
     'media_object.h',
diff --git a/src/libcamera/mailbox.cpp b/src/libcamera/mailbox.cpp
new file mode 100644
index 00000000..4d129224
--- /dev/null
+++ b/src/libcamera/mailbox.cpp
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Google Inc.
+ *
+ * mailbox.cpp - Template class for generic mailbox
+ */
+
+#include "libcamera/internal/mailbox.h"
+
+#include <libcamera/base/log.h>
+
+namespace libcamera {
+
+/**
+ * \class MailBox
+ * \brief MailBox of data
+ *
+ * A MailBox contains a block of data that has a single producer, and one or
+ * multiple consumers. It's often used as a shared_ptr (SharedMailBox) and
+ * being held by different tasks in pipelines.
+ */
+
+/**
+ * \typedef MailBox::Recycler
+ * \brief A function that recycles the data when the mailbox is destructed
+ */
+
+template<class T>
+MailBox<T>::~MailBox()
+{
+	if (valid_ && recycler_)
+		recycler_(item_);
+}
+
+/**
+ * \brief Set the data as the producer. Should be called only once
+ * \param[in] item The data to be stored
+ * \param[in] recycler The function that recycles \a data when destructing the
+ * mailbox. Mostly used for recycling buffers.
+ */
+template<class T>
+void MailBox<T>::put(const T &item, MailBox<T>::Recycler recycler)
+{
+	ASSERT(!valid_);
+
+	valid_ = true;
+	recycler_ = recycler;
+	item_ = item;
+}
+
+/**
+ * \brief Get the data as a consumer. put() function should be called ahead
+ *
+ * \return The stored data
+ */
+template<class T>
+const T &MailBox<T>::get()
+{
+	ASSERT(valid_);
+	return item_;
+}
+
+/**
+ * \fn MailBox::valid()
+ * \return True if put() function has been called
+ */
+
+/**
+ * \typedef SharedMailBox
+ * \brief A mailbox as a shared_ptr
+ */
+
+/**
+ * \fn makeMailBox()
+ * \brief A helper function to create a SharedMailBox
+ *
+ * \return A mailbox as a SharedMailBox
+ */
+
+/**
+ * \brief A helper function to create a list of mailboxes
+ * \param[in] count The number of mailboxes requested
+ *
+ * \return Mailboxes as a vector
+ */
+template<class T>
+std::vector<SharedMailBox<T>> makeMailBoxVector(const unsigned int count)
+{
+	std::vector<SharedMailBox<T>> mailBoxes;
+	mailBoxes.resize(count);
+	for (unsigned int i = 0; i < count; i++)
+		mailBoxes[i] = makeMailBox<T>();
+
+	return mailBoxes;
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index aa9ab029..6c3ef537 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -38,6 +38,7 @@ libcamera_internal_sources = files([
     'ipc_pipe.cpp',
     'ipc_pipe_unixsocket.cpp',
     'ipc_unixsocket.cpp',
+    'mailbox.cpp',
     'mapped_framebuffer.cpp',
     'media_device.cpp',
     'media_object.cpp',
