[libcamera-devel] test: threads: Test thread cleanup upon abnormal termination
diff mbox series

Message ID 20221003235843.19175-1-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • [libcamera-devel] test: threads: Test thread cleanup upon abnormal termination
Related show

Commit Message

Laurent Pinchart Oct. 3, 2022, 11:58 p.m. UTC
If a thread ends abnormally (that is, without retuning normally from its
run() function, for instance with a direct call to pthread_cancel()),
thread cleanup should still be performed. Add a test to ensure this.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 test/threads.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

Comments

Umang Jain Oct. 4, 2022, 2:05 p.m. UTC | #1
Hi Laurent,

On 10/4/22 5:28 AM, Laurent Pinchart via libcamera-devel wrote:
> If a thread ends abnormally (that is, without retuning normally from its
> run() function, for instance with a direct call to pthread_cancel()),
> thread cleanup should still be performed. Add a test to ensure this.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>   test/threads.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 44 insertions(+)
>
> diff --git a/test/threads.cpp b/test/threads.cpp
> index d83b5833998f..7daa76edb00e 100644
> --- a/test/threads.cpp
> +++ b/test/threads.cpp
> @@ -8,7 +8,9 @@
>   #include <chrono>
>   #include <iostream>
>   #include <memory>
> +#include <pthread.h>
>   #include <thread>
> +#include <time.h>
>   
>   #include <libcamera/base/thread.h>
>   
> @@ -35,6 +37,32 @@ private:
>   	chrono::steady_clock::duration duration_;
>   };
>   
> +class CancelThread : public Thread
> +{
> +public:
> +	CancelThread(bool &cancelled)
> +		: cancelled_(cancelled)
> +	{
> +	}
> +
> +protected:
> +	void run()
> +	{
> +		cancelled_ = true;
> +
> +		/* Cancel the thread can call a guaranteed cancellation point. */
> +		pthread_cancel(pthread_self());
> +
> +		struct timespec req{ 0, 100*000*000 };
> +		nanosleep(&req, nullptr);

Took a bit of reading to determine that nanosleep() is a cancellation 
point :)
Maybe it deserves a comment as well, upto you.

Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> +
> +		cancelled_ = false;
> +	}
> +
> +private:
> +	bool &cancelled_;
> +};
> +
>   class ThreadTest : public Test
>   {
>   protected:
> @@ -118,6 +146,22 @@ protected:
>   			return TestFail;
>   		}
>   
> +		/* Test thread cleanup upon abnormal termination. */
> +		bool cancelled = false;
> +		bool finished = false;
> +
> +		thread = std::make_unique<CancelThread>(cancelled);
> +		thread->finished.connect(this, [&finished]() { finished = true; });
> +
> +		thread->start();
> +		thread->exit(0);
> +		thread->wait(chrono::milliseconds(1000));
> +
> +		if (!cancelled || !finished) {
> +			cout << "Cleanup failed upon abnormal termination" << endl;
> +			return TestFail;
> +		}
> +
>   		return TestPass;
>   	}
>
Laurent Pinchart Oct. 4, 2022, 2:13 p.m. UTC | #2
On Tue, Oct 04, 2022 at 07:35:42PM +0530, Umang Jain wrote:
> On 10/4/22 5:28 AM, Laurent Pinchart via libcamera-devel wrote:
> > If a thread ends abnormally (that is, without retuning normally from its
> > run() function, for instance with a direct call to pthread_cancel()),
> > thread cleanup should still be performed. Add a test to ensure this.
> >
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >   test/threads.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 44 insertions(+)
> >
> > diff --git a/test/threads.cpp b/test/threads.cpp
> > index d83b5833998f..7daa76edb00e 100644
> > --- a/test/threads.cpp
> > +++ b/test/threads.cpp
> > @@ -8,7 +8,9 @@
> >   #include <chrono>
> >   #include <iostream>
> >   #include <memory>
> > +#include <pthread.h>
> >   #include <thread>
> > +#include <time.h>
> >   
> >   #include <libcamera/base/thread.h>
> >   
> > @@ -35,6 +37,32 @@ private:
> >   	chrono::steady_clock::duration duration_;
> >   };
> >   
> > +class CancelThread : public Thread
> > +{
> > +public:
> > +	CancelThread(bool &cancelled)
> > +		: cancelled_(cancelled)
> > +	{
> > +	}
> > +
> > +protected:
> > +	void run()
> > +	{
> > +		cancelled_ = true;
> > +
> > +		/* Cancel the thread can call a guaranteed cancellation point. */

I meant s/can/and/. I'll turn this into

		/*
		 * Cancel the thread can call a guaranteed cancellation point
		 *(nanosleep).
		 */

> > +		pthread_cancel(pthread_self());
> > +
> > +		struct timespec req{ 0, 100*000*000 };
> > +		nanosleep(&req, nullptr);
> 
> Took a bit of reading to determine that nanosleep() is a cancellation 
> point :)
> Maybe it deserves a comment as well, upto you.
> 
> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> 
> > +
> > +		cancelled_ = false;
> > +	}
> > +
> > +private:
> > +	bool &cancelled_;
> > +};
> > +
> >   class ThreadTest : public Test
> >   {
> >   protected:
> > @@ -118,6 +146,22 @@ protected:
> >   			return TestFail;
> >   		}
> >   
> > +		/* Test thread cleanup upon abnormal termination. */
> > +		bool cancelled = false;
> > +		bool finished = false;
> > +
> > +		thread = std::make_unique<CancelThread>(cancelled);
> > +		thread->finished.connect(this, [&finished]() { finished = true; });
> > +
> > +		thread->start();
> > +		thread->exit(0);
> > +		thread->wait(chrono::milliseconds(1000));
> > +
> > +		if (!cancelled || !finished) {
> > +			cout << "Cleanup failed upon abnormal termination" << endl;
> > +			return TestFail;
> > +		}
> > +
> >   		return TestPass;
> >   	}
> >
Umang Jain Oct. 4, 2022, 2:14 p.m. UTC | #3
Hi,

On 10/4/22 7:43 PM, Laurent Pinchart wrote:
> On Tue, Oct 04, 2022 at 07:35:42PM +0530, Umang Jain wrote:
>> On 10/4/22 5:28 AM, Laurent Pinchart via libcamera-devel wrote:
>>> If a thread ends abnormally (that is, without retuning normally from its
>>> run() function, for instance with a direct call to pthread_cancel()),
>>> thread cleanup should still be performed. Add a test to ensure this.
>>>
>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>> ---
>>>    test/threads.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++
>>>    1 file changed, 44 insertions(+)
>>>
>>> diff --git a/test/threads.cpp b/test/threads.cpp
>>> index d83b5833998f..7daa76edb00e 100644
>>> --- a/test/threads.cpp
>>> +++ b/test/threads.cpp
>>> @@ -8,7 +8,9 @@
>>>    #include <chrono>
>>>    #include <iostream>
>>>    #include <memory>
>>> +#include <pthread.h>
>>>    #include <thread>
>>> +#include <time.h>
>>>    
>>>    #include <libcamera/base/thread.h>
>>>    
>>> @@ -35,6 +37,32 @@ private:
>>>    	chrono::steady_clock::duration duration_;
>>>    };
>>>    
>>> +class CancelThread : public Thread
>>> +{
>>> +public:
>>> +	CancelThread(bool &cancelled)
>>> +		: cancelled_(cancelled)
>>> +	{
>>> +	}
>>> +
>>> +protected:
>>> +	void run()
>>> +	{
>>> +		cancelled_ = true;
>>> +
>>> +		/* Cancel the thread can call a guaranteed cancellation point. */
> I meant s/can/and/. I'll turn this into
>
> 		/*
> 		 * Cancel the thread can call a guaranteed cancellation point
> 		 *(nanosleep).
> 		 */

Perfect, thanks!
>>> +		pthread_cancel(pthread_self());
>>> +
>>> +		struct timespec req{ 0, 100*000*000 };
>>> +		nanosleep(&req, nullptr);
>> Took a bit of reading to determine that nanosleep() is a cancellation
>> point :)
>> Maybe it deserves a comment as well, upto you.
>>
>> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
>>
>>> +
>>> +		cancelled_ = false;
>>> +	}
>>> +
>>> +private:
>>> +	bool &cancelled_;
>>> +};
>>> +
>>>    class ThreadTest : public Test
>>>    {
>>>    protected:
>>> @@ -118,6 +146,22 @@ protected:
>>>    			return TestFail;
>>>    		}
>>>    
>>> +		/* Test thread cleanup upon abnormal termination. */
>>> +		bool cancelled = false;
>>> +		bool finished = false;
>>> +
>>> +		thread = std::make_unique<CancelThread>(cancelled);
>>> +		thread->finished.connect(this, [&finished]() { finished = true; });
>>> +
>>> +		thread->start();
>>> +		thread->exit(0);
>>> +		thread->wait(chrono::milliseconds(1000));
>>> +
>>> +		if (!cancelled || !finished) {
>>> +			cout << "Cleanup failed upon abnormal termination" << endl;
>>> +			return TestFail;
>>> +		}
>>> +
>>>    		return TestPass;
>>>    	}
>>>

Patch
diff mbox series

diff --git a/test/threads.cpp b/test/threads.cpp
index d83b5833998f..7daa76edb00e 100644
--- a/test/threads.cpp
+++ b/test/threads.cpp
@@ -8,7 +8,9 @@ 
 #include <chrono>
 #include <iostream>
 #include <memory>
+#include <pthread.h>
 #include <thread>
+#include <time.h>
 
 #include <libcamera/base/thread.h>
 
@@ -35,6 +37,32 @@  private:
 	chrono::steady_clock::duration duration_;
 };
 
+class CancelThread : public Thread
+{
+public:
+	CancelThread(bool &cancelled)
+		: cancelled_(cancelled)
+	{
+	}
+
+protected:
+	void run()
+	{
+		cancelled_ = true;
+
+		/* Cancel the thread can call a guaranteed cancellation point. */
+		pthread_cancel(pthread_self());
+
+		struct timespec req{ 0, 100*000*000 };
+		nanosleep(&req, nullptr);
+
+		cancelled_ = false;
+	}
+
+private:
+	bool &cancelled_;
+};
+
 class ThreadTest : public Test
 {
 protected:
@@ -118,6 +146,22 @@  protected:
 			return TestFail;
 		}
 
+		/* Test thread cleanup upon abnormal termination. */
+		bool cancelled = false;
+		bool finished = false;
+
+		thread = std::make_unique<CancelThread>(cancelled);
+		thread->finished.connect(this, [&finished]() { finished = true; });
+
+		thread->start();
+		thread->exit(0);
+		thread->wait(chrono::milliseconds(1000));
+
+		if (!cancelled || !finished) {
+			cout << "Cleanup failed upon abnormal termination" << endl;
+			return TestFail;
+		}
+
 		return TestPass;
 	}