diff --git a/src/libcamera/bound_method.cpp b/src/libcamera/bound_method.cpp
index e18c2eb4c68e..9aa59dc3678f 100644
--- a/src/libcamera/bound_method.cpp
+++ b/src/libcamera/bound_method.cpp
@@ -35,16 +35,19 @@ namespace libcamera {
  * thread.
  *
  * \var ConnectionType::ConnectionTypeQueued
- * \brief The receiver is invoked asynchronously in its thread when control
- * returns to the thread's event loop. The sender proceeds without waiting for
- * the invocation to complete.
+ * \brief The receiver is invoked asynchronously
+ *
+ * Invoke the receiver asynchronously in its thread when control returns to the
+ * thread's event loop. The sender proceeds without waiting for the invocation
+ * to complete.
  *
  * \var ConnectionType::ConnectionTypeBlocking
- * \brief The receiver is invoked asynchronously in its thread when control
- * returns to the thread's event loop. The sender blocks until the receiver
- * signals the completion of the invocation. This connection type shall not be
- * used when the sender and receiver live in the same thread, otherwise
- * deadlock will occur.
+ * \brief The receiver is invoked synchronously
+ *
+ * If the sender and the receiver live in the same thread, this is equivalent to
+ * ConnectionTypeDirect. Otherwise, the receiver is invoked asynchronously in
+ * its thread when control returns to the thread's event loop. The sender
+ * blocks until the receiver signals the completion of the invocation.
  */
 
 /**
@@ -71,6 +74,9 @@ bool BoundMethodBase::activatePack(std::shared_ptr<BoundMethodPackBase> pack,
 			type = ConnectionTypeDirect;
 		else
 			type = ConnectionTypeQueued;
+	} else if (type == ConnectionTypeBlocking) {
+		if (Thread::current() == object_->thread())
+			type = ConnectionTypeDirect;
 	}
 
 	switch (type) {
diff --git a/test/object-invoke.cpp b/test/object-invoke.cpp
index 8e2055ca620f..fa162c838c78 100644
--- a/test/object-invoke.cpp
+++ b/test/object-invoke.cpp
@@ -100,6 +100,26 @@ protected:
 			return TestFail;
 		}
 
+		/*
+		 * Test that blocking invocation is delivered directly when the
+		 * caller and callee live in the same thread.
+		 */
+		object_.reset();
+
+		object_.invokeMethod(&InvokedObject::method,
+				     ConnectionTypeBlocking, 42);
+
+		switch (object_.status()) {
+		case InvokedObject::NoCall:
+			cout << "Method not invoked for main thread (blocking)" << endl;
+			return TestFail;
+		case InvokedObject::InvalidThread:
+			cout << "Method invoked in incorrect thread for main thread (blocking)" << endl;
+			return TestFail;
+		default:
+			break;
+		}
+
 		/*
 		 * Move the object to a thread and verify that auto method
 		 * invocation is delivered in the correct thread.
