[libcamera-devel] [PATCH 2/3] test: message: Test recursive Thread::dispatchMessages() calls
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Mon Jul 5 14:16:32 CEST 2021
Hi Jacopo,
On Mon, Jul 05, 2021 at 01:08:29PM +0200, Jacopo Mondi wrote:
> On Fri, Jul 02, 2021 at 02:07:40AM +0300, Laurent Pinchart wrote:
> > The Thread::dispatchMessages() function needs to support recursive
> > calls, for instance to allow flushing delivery of invoked methods. Add a
> > corresponding test, which currently fails with a double free.
> >
> > Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> > ---
> > test/message.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++--
> > 1 file changed, 64 insertions(+), 2 deletions(-)
> >
> > diff --git a/test/message.cpp b/test/message.cpp
> > index eeea57feab76..7895cd7c2106 100644
> > --- a/test/message.cpp
> > +++ b/test/message.cpp
> > @@ -26,8 +26,8 @@ public:
> > MessageReceived,
> > };
> >
> > - MessageReceiver()
> > - : status_(NoMessage)
> > + MessageReceiver(Object *parent = nullptr)
> > + : Object(parent), status_(NoMessage)
> > {
> > }
> >
> > @@ -52,6 +52,45 @@ private:
> > Status status_;
> > };
> >
> > +class RecursiveMessageReceiver : public Object
> > +{
> > +public:
> > + RecursiveMessageReceiver()
> > + : child_(this), success_(false)
> > + {
> > + }
> > +
> > + bool success() const { return success_; }
> > +
> > +protected:
> > + void message([[maybe_unused]] Message *msg)
> > + {
> > + if (msg->type() != Message::None) {
> > + Object::message(msg);
> > + return;
> > + }
> > +
> > + child_.postMessage(std::make_unique<Message>(Message::None));
> > +
> > + /*
> > + * If the child has already received the message, something is
> > + * wrong.
> > + */
> > + if (child_.status() != MessageReceiver::NoMessage)
> > + return;
> > +
> > + Thread::current()->dispatchMessages(Message::None);
> > +
> > + /* The child should now have received the message. */
> > + if (child_.status() == MessageReceiver::MessageReceived)
> > + success_ = true;
> > + }
> > +
> > +private:
> > + MessageReceiver child_;
> > + bool success_;
> > +};
> > +
> > class SlowMessageReceiver : public Object
> > {
> > protected:
> > @@ -120,6 +159,29 @@ protected:
> >
> > delete slowReceiver;
> >
> > + this_thread::sleep_for(chrono::milliseconds(100));
> > +
> > + /*
> > + * Test recursive calls to Thread::dispatchMessages(). Messages
> > + * should be delivered correctly, without crashes or memory
> > + * leaks. Two messages need to be posted to ensure we don't only
> > + * test the simple case of a queue containing a single message.
> > + */
> > + RecursiveMessageReceiver *recursiveReceiver = new RecursiveMessageReceiver();
> > + recursiveReceiver->moveToThread(&thread_);
> > +
> > + recursiveReceiver->postMessage(std::make_unique<Message>(Message::None));
> > + recursiveReceiver->postMessage(std::make_unique<Message>(Message::UserMessage));
>
> I'm not sure why two messages are necessary, also considering that
> messages != Message::None are ignored...
>
> If my understanding is correct, a single postMessage() triggers a
> dispatcher->interrupt() which triggers a dispatchMessages() as the receiver
> runs on a different thread and can be interrupted. The message is then
> dispatched and handled by the execution of RecursiveMessageReceiver::message()
> call.
>
> From there we post a message to the child and go throuh the same path if not
> that the child runs on the same thread and we have to dispatch
> messages forcefully, causing a recursive call to dispatchMessages() on
> the same thread of execution.
>
> Am I missing something or is the second message not required ?
The second message is needed to ensure we don't only test a case where
the queue has a single message. The test doesn't fail in that case, due
to the fact that the internal iter variable in
Thread::dispatchMessages() is incremented before dispatching the current
message. If the list contains a single message, iter will thus be the
end() of the list after being incremented, which will not cause the
outer dispatchMessage() call to fail with use-after-free (the .end()
iterator is not invalidated by an erase operation on a std::list).
> > +
> > + this_thread::sleep_for(chrono::milliseconds(10));
> > +
> > + if (!recursiveReceiver->success()) {
> > + cout << "Recursive message delivery failed" << endl;
> > + return TestFail;
> > + }
> > +
> > + delete recursiveReceiver;
> > +
> > return TestPass;
> > }
> >
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list