Jump to content
Roger Cigol

std::lock_guard<std::mutex> throwing exceptions under windows

Recommended Posts

template<class A>class TThreadDataInterface_TS
{
private:
   std::mutex DataMutex;
   A ThreadSafeCopyOfData;
   A &operator=(A RHS); // declared private - not defined - prevents use of = operator
public:
   void Read_TS(A &Data) // read (copy) Data out, thread safe, no handshake of data
      {
         try {
            const std::lock_guard<std::mutex> Lock(DataMutex);
            Data = ThreadSafeCopyOfData;
         } catch (...) {
            A UninitialisedData;
            Data = UninitialisedData;
         }
      }
   void Write_TS(const A &Data) // write (copy) Data in, thread safe, no handshake of data
      {
         try {
            const std::lock_guard<std::mutex> Lock(DataMutex);
            ThreadSafeCopyOfData = Data;
         } catch (...) {
         }
      }
   TThreadDataInterface_TS(void){}
   TThreadDataInterface_TS(const A &InitialValue){
      ThreadSafeCopyOfData = InitialValue;}
}

I have ported the above template class from a GNU / Linux working code base to an Emabarcadero Clang 64 bit Windows console program. It's a template class for use communicating data (type A) between two threads. The GNU/Linux code did not have any try / catch statements in and worked fine. When I ran this code using Embarcadero Clang64 I found that most of the time the locking worked fine but sometimes the line "const std::lock_guard<std::mutex> Lock(DataMutex);" caused an exception to be thrown (and since this was not caught anywhere the program abnormally terminated). I added the try /catch that you see above and now the program runs without aborting - but as you can see from the code on the rare times that an exception is caught the data transfer doesn't work smoothly. The bottom of this page:

CPP ref lock_guard constructor seems to state that "if m is not currently locked by the calling thread, the constructor never throws exceptions (no-throw guarantee)." I am sure that my thread has not previously locked the mutex so I am puzzled as to why I am sometimes (1 in a 100?) getting this exception. Has anyone got any thoughts on this ?

Share this post


Link to post
4 hours ago, Roger Cigol said:

The GNU/Linux code did not have any try / catch statements in and worked fine. When I ran this code using Embarcadero Clang64 I found that most of the time the locking worked fine but sometimes the line "const std::lock_guard<std::mutex> Lock(DataMutex);" caused an exception to be thrown (and since this was not caught anywhere the program abnormally terminated).

You did not show your code that is using TThreadDataInterface_TS, but I would assume a bug in your code before I would assume a bug in the standard library.

4 hours ago, Roger Cigol said:

I added the try /catch that you see above and now the program runs without aborting - but as you can see from the code on the rare times that an exception is caught the data transfer doesn't work smoothly. The bottom of this page:

CPP ref lock_guard constructor seems to state that "if m is not currently locked by the calling thread, the constructor never throws exceptions (no-throw guarantee)." I am sure that my thread has not previously locked the mutex so I am puzzled as to why I am sometimes (1 in a 100?) getting this exception.

This kind of random behavior is a good indicator that your code is experiencing undefined behavior.  Perhaps you are calling Write_TS() on an invalid TThreadDataInterface_TS object?  Maybe some piece of code has previously corrupted memory and TThreadDataInterface_TS is the unwitting victim?  There are many possibilities.  But they all stem from one fact - you have a bug SOMEWHERE in your code, and this behavior is the RESULT of that bug.

Share this post


Link to post

Hi Remy, Thanks for your valuable input. I am aware I haven't offered a "minimum reproduceable error" set of code for my posting. This is my next step - just two threads - one passing a number to the other using my interface class. I will do this later this week and post it up here (assuming I get the same error). The code I do have which is showing the problem is a googletest framework unit test which seems to run ok on gnu/linux but not on Embarcadero - but I agree this could still be a bug in my code.....

Share this post


Link to post

For anyone interested I have solved my problem. I have an abstract base class (called TThreadStateMachine) that I use to derive classes for code units that run in their own threads and are state machine based control. The base class has a virtual function that forms the basic periodic tick of the derived class state machine.  The  TThreadDataInterface_TS template class is used to provide thread safe communications to / from the state machine.

The problem comes when the derived classes, based on TThreadStateMachine are destroyed. The C++ standard ensures that the derived class destructor is called BEFORE the base class destructor. But because the base class was still running it's own thread it was (sometimes) causing the derived class state machine to "Tick" even when parts of it have been destroyed. My solution to this was to create a new base class function AbortThread() and make sure I call this in the derived class destructor before doing any further destruction. Now it passes many many iterations of a unit test creating and destroying without an error. I believe all is good now.

 

One mystery may never be solved.:Why dic my poor original implementation never seem to cause a problem on Linux / GNU C++ ?

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×