diff --git a/include/libcamera/internal/message.h b/include/libcamera/internal/message.h
index 92ea64a..b8d9866 100644
--- a/include/libcamera/internal/message.h
+++ b/include/libcamera/internal/message.h
@@ -25,6 +25,7 @@ public:
 		None = 0,
 		InvokeMessage = 1,
 		ThreadMoveMessage = 2,
+		CatchAll = 999,
 		UserMessage = 1000,
 	};
 
diff --git a/include/libcamera/internal/thread.h b/include/libcamera/internal/thread.h
index 7b59e58..1dfeb72 100644
--- a/include/libcamera/internal/thread.h
+++ b/include/libcamera/internal/thread.h
@@ -14,6 +14,7 @@
 
 #include <libcamera/signal.h>
 
+#include "libcamera/internal/message.h"
 #include "libcamera/internal/utils.h"
 
 namespace libcamera {
@@ -47,7 +48,7 @@ public:
 	EventDispatcher *eventDispatcher();
 	void setEventDispatcher(std::unique_ptr<EventDispatcher> dispatcher);
 
-	void dispatchMessages();
+	void dispatchMessages(Message::Type type);
 
 protected:
 	int exec();
diff --git a/src/libcamera/event_dispatcher_poll.cpp b/src/libcamera/event_dispatcher_poll.cpp
index 9ab85da..b9fabf8 100644
--- a/src/libcamera/event_dispatcher_poll.cpp
+++ b/src/libcamera/event_dispatcher_poll.cpp
@@ -146,7 +146,7 @@ void EventDispatcherPoll::processEvents()
 {
 	int ret;
 
-	Thread::current()->dispatchMessages();
+	Thread::current()->dispatchMessages(Message::Type::CatchAll);
 
 	/* Create the pollfd array. */
 	std::vector<struct pollfd> pollfds;
diff --git a/src/libcamera/message.cpp b/src/libcamera/message.cpp
index e9b3e73..e462f90 100644
--- a/src/libcamera/message.cpp
+++ b/src/libcamera/message.cpp
@@ -49,6 +49,8 @@ std::atomic_uint Message::nextUserType_{ Message::UserMessage };
  * \brief Asynchronous method invocation across threads
  * \var Message::ThreadMoveMessage
  * \brief Object is being moved to a different thread
+ * \var Message::CatchAll
+ * \brief Helper to match message of any type
  * \var Message::UserMessage
  * \brief First value available for user-defined messages
  */
diff --git a/src/libcamera/thread.cpp b/src/libcamera/thread.cpp
index d1750d7..d3a5f81 100644
--- a/src/libcamera/thread.cpp
+++ b/src/libcamera/thread.cpp
@@ -552,26 +552,47 @@ void Thread::removeMessages(Object *receiver)
 }
 
 /**
- * \brief Dispatch all posted messages for this thread
+ * \brief Dispatch posted messages for this thread as per \a type Message::Type.
+ *  Pass Message::Type::CatchAll to dispatch every posted message posted for
+ *  this thread.
  */
-void Thread::dispatchMessages()
+void Thread::dispatchMessages(Message::Type type)
 {
 	MutexLocker locker(data_->messages_.mutex_);
 
-	while (!data_->messages_.list_.empty()) {
-		std::unique_ptr<Message> msg = std::move(data_->messages_.list_.front());
-		data_->messages_.list_.pop_front();
-		if (!msg)
-			continue;
+	if (type == Message::Type::CatchAll) {
+		while (!data_->messages_.list_.empty()) {
+			std::unique_ptr<Message> msg =
+				std::move(data_->messages_.list_.front());
+			data_->messages_.list_.pop_front();
+			if (!msg)
+				continue;
 
-		Object *receiver = msg->receiver_;
-		ASSERT(data_ == receiver->thread()->data_);
+			Object *receiver = msg->receiver_;
+			ASSERT(data_ == receiver->thread()->data_);
 
-		receiver->pendingMessages_--;
+			receiver->pendingMessages_--;
 
-		locker.unlock();
-		receiver->message(msg.get());
-		locker.lock();
+			locker.unlock();
+			receiver->message(msg.get());
+			locker.lock();
+		}
+	} else {
+		for (std::unique_ptr<Message> &msg : data_->messages_.list_) {
+			if (!msg)
+				continue;
+
+			if (msg->type() == type) {
+				std::unique_ptr<Message> message = std::move(msg);
+				Object *receiver = message->receiver_;
+				ASSERT(data_ == receiver->thread()->data_);
+				receiver->pendingMessages_--;
+
+				locker.unlock();
+				receiver->message(message.get());
+				locker.lock();
+			}
+		}
 	}
 }
 
