diff --git a/include/libcamera/internal/message.h b/include/libcamera/internal/message.h
index 92ea64a..f1b133b 100644
--- a/include/libcamera/internal/message.h
+++ b/include/libcamera/internal/message.h
@@ -25,6 +25,7 @@ public:
 		None = 0,
 		InvokeMessage = 1,
 		ThreadMoveMessage = 2,
+		DeferredDelete = 3,
 		UserMessage = 1000,
 	};
 
diff --git a/include/libcamera/object.h b/include/libcamera/object.h
index 9a3dd07..a1882f0 100644
--- a/include/libcamera/object.h
+++ b/include/libcamera/object.h
@@ -27,6 +27,8 @@ public:
 	Object(Object *parent = nullptr);
 	virtual ~Object();
 
+	void deleteLater();
+
 	void postMessage(std::unique_ptr<Message> msg);
 
 	template<typename T, typename R, typename... FuncArgs, typename... Args,
diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp
index 1544a23..93e683a 100644
--- a/src/libcamera/object.cpp
+++ b/src/libcamera/object.cpp
@@ -98,6 +98,22 @@ Object::~Object()
 		child->parent_ = nullptr;
 }
 
+/**
+ * \brief Defer the deletion of Object instance to their thread
+ *
+ * Schedule deletion of the Object in the thread to which they are bound.
+ * This will prevent destroying the Object from a different thread, which is
+ * not allowed by the threading model.
+ *
+ * It will typically be used with a custom deleter when creating a shared
+ * pointer. It can then be ensured that the Object is deleted in its
+ * own thread even if its last reference is dropped in a different thread.
+ */
+void Object::deleteLater()
+{
+	postMessage(std::make_unique<Message>(Message::DeferredDelete));
+}
+
 /**
  * \brief Post a message to the object's thread
  * \param[in] msg The message
@@ -143,6 +159,10 @@ void Object::message(Message *msg)
 
 		break;
 	}
+	case Message::DeferredDelete: {
+		delete this;
+		break;
+	}
 
 	default:
 		break;
