diff options
-rw-r--r-- | src/libcamera/base/thread.cpp | 43 |
1 files changed, 33 insertions, 10 deletions
diff --git a/src/libcamera/base/thread.cpp b/src/libcamera/base/thread.cpp index 7f791152..1232f895 100644 --- a/src/libcamera/base/thread.cpp +++ b/src/libcamera/base/thread.cpp @@ -126,6 +126,11 @@ public: * \brief Protects the \ref list_ */ Mutex mutex_; + /** + * \brief The recursion level for recursive Thread::dispatchMessages() + * calls + */ + unsigned int recursion_ = 0; }; /** @@ -595,30 +600,34 @@ void Thread::removeMessages(Object *receiver) * Messages shall only be dispatched from the current thread, typically within * the thread from the run() function. Calling this function outside of the * thread results in undefined behaviour. + * + * This function is not thread-safe, but it may be called recursively in the + * same thread from an object's message handler. It guarantees delivery of + * messages in the order they have been posted in all cases. */ void Thread::dispatchMessages(Message::Type type) { ASSERT(data_ == ThreadData::current()); + ++data_->messages_.recursion_; + MutexLocker locker(data_->messages_.mutex_); std::list<std::unique_ptr<Message>> &messages = data_->messages_.list_; - for (auto iter = messages.begin(); iter != messages.end(); ) { - std::unique_ptr<Message> &msg = *iter; - - if (!msg) { - iter = data_->messages_.list_.erase(iter); + for (std::unique_ptr<Message> &msg : messages) { + if (!msg) continue; - } - if (type != Message::Type::None && msg->type() != type) { - ++iter; + if (type != Message::Type::None && msg->type() != type) continue; - } + /* + * Move the message, setting the entry in the list to null. It + * will cause recursive calls to ignore the entry, and the erase + * loop at the end of the function to delete it from the list. + */ std::unique_ptr<Message> message = std::move(msg); - iter = data_->messages_.list_.erase(iter); Object *receiver = message->receiver_; ASSERT(data_ == receiver->thread()->data_); @@ -629,6 +638,20 @@ void Thread::dispatchMessages(Message::Type type) message.reset(); locker.lock(); } + + /* + * If the recursion level is 0, erase all null messages in the list. We + * can't do so during recursion, as it would invalidate the iterator of + * the outer calls. + */ + if (!--data_->messages_.recursion_) { + for (auto iter = messages.begin(); iter != messages.end(); ) { + if (!*iter) + iter = messages.erase(iter); + else + ++iter; + } + } } /** |