Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Non-Reentrant Class Use In Multithreaded Program
Forum Updated to NodeBB v4.3 + New Features

Non-Reentrant Class Use In Multithreaded Program

Scheduled Pinned Locked Moved Solved General and Desktop
64 Posts 6 Posters 19.6k Views 4 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    Crag_Hack
    wrote on last edited by
    #1

    I have a quick question - I have a multithreaded program with a worker thread doing file backup operations and a main thread doing the GUI and controlling the behavior of the worker thread. Is it OK to use an instance of a non-reentrant Qt class - in this case QStorageInfo - in the main GUI thread as long as I only use it in that thread and don't touch it in the worker thread? No race conditions can happen right?
    Thanks

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      If you don't share that object with any other thread, then no, no problem.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      2
      • C Offline
        C Offline
        Crag_Hack
        wrote on last edited by
        #3

        Thanks SGaist guru!

        A followup question that's been nagging me - if you go here it says:

        By extension, a class is said to be reentrant if its member functions can be called safely from multiple threads, as long as each thread uses a different instance of the class.
        

        Does that mean if a class in not reentrant like QStorageInfo it is further restricted so that objects of that class should only be used from one thread? Can objects of that class be used in different threads as long as they each access their own instances? It's a little misleading how a non-reentrant class compares to a reentrant one based on what I've read.

        kshegunovK 1 Reply Last reply
        0
        • C Crag_Hack

          Thanks SGaist guru!

          A followup question that's been nagging me - if you go here it says:

          By extension, a class is said to be reentrant if its member functions can be called safely from multiple threads, as long as each thread uses a different instance of the class.
          

          Does that mean if a class in not reentrant like QStorageInfo it is further restricted so that objects of that class should only be used from one thread? Can objects of that class be used in different threads as long as they each access their own instances? It's a little misleading how a non-reentrant class compares to a reentrant one based on what I've read.

          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by
          #4

          @Crag_Hack said in Non-Reentrant Class Use In Multithreaded Program:

          Does that mean if a class in not reentrant like QStorageInfo it is further restricted so that objects of that class should only be used from one thread?

          Yes, it does.

          Can objects of that class be used in different threads as long as they each access their own instances?

          If they aren't reentrant, no, they can't.

          It's a little misleading how a non-reentrant class compares to a reentrant one based on what I've read.

          To be reentrant basically it means that the class doesn't use any global (program-wise) resources, e. g. static variables. If that's not true then all the instances of the class are using data they shared, and if the class isn't specifically made to be thread-safe it means the instances of that class can be called upon only from one thread! The widgets (GUI) classes are all like this, they're neither reentrant nor thread-safe, meaning you can only access them from the main (GUI) thread.

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          3
          • C Offline
            C Offline
            Crag_Hack
            wrote on last edited by
            #5

            Thanks kshegunov. Sounds like a pretty serious restriction that should be documented on that page I linked to.

            kshegunovK 1 Reply Last reply
            0
            • C Crag_Hack

              Thanks kshegunov. Sounds like a pretty serious restriction that should be documented on that page I linked to.

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by
              #6

              It is. Here:

              Note: Qt classes are only documented as thread-safe if they are intended to be used by multiple threads. If a function is not marked as thread-safe or reentrant, it should not be used from different threads. If a class is not marked as thread-safe or reentrant then a specific instance of that class should not be accessed from different threads.

              And there's more at the bottom in the "Notes about Qt classes".

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              2
              • C Offline
                C Offline
                Crag_Hack
                wrote on last edited by
                #7

                It just says a specific instance not separate instances though right? From what you said before implies that non-reentrant class objects cannot even be accessed from different threads even if the instances are isolated to their own thread.

                kshegunovK 1 Reply Last reply
                0
                • C Crag_Hack

                  It just says a specific instance not separate instances though right? From what you said before implies that non-reentrant class objects cannot even be accessed from different threads even if the instances are isolated to their own thread.

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by kshegunov
                  #8

                  @Crag_Hack said in Non-Reentrant Class Use In Multithreaded Program:

                  It just says a specific instance not separate instances though right?

                  It's just the turn of phrase. "specific" here has the meaning of "a", "one" or "any".

                  From what you said before implies that non-reentrant class objects cannot even be accessed from different threads even if the instances are isolated to their own thread.

                  Yes, this is sort of what a definition of (non)reentrant is. If you want to get the formal one, here it goes:

                  A reentrant function is such a function whose effect, when called by two or more threads, is guaranteed to be as if the threads each executed the function one after another in an undefined order, even if the actual execution is interleaved

                  As per the POSIX C standard: ISO/IEC 9945:1-1996, §2.2.2

                  Or in other words, a function (or method in the context of C++) can be interrupted (e. g. by a context switch) and then re-entered (hence the name) without introducing side effects.

                  I personally find that the way it's written in Qt's documentation is more understandable to most, but both definitions are pretty much equivalent.

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  2
                  • C Offline
                    C Offline
                    Crag_Hack
                    wrote on last edited by
                    #9

                    Ah I see, just a misunderstanding of the language on my behalf.

                    C 1 Reply Last reply
                    0
                    • C Crag_Hack

                      Ah I see, just a misunderstanding of the language on my behalf.

                      C Offline
                      C Offline
                      Crag_Hack
                      wrote on last edited by
                      #10

                      Quick follow-up question - it's safe to use qSort and foreach loops in my worker thread right? The reason I ask about foreach is I heard iterators in Qt are not safe to use in multiple threads.

                      kshegunovK 1 Reply Last reply
                      0
                      • C Crag_Hack

                        Quick follow-up question - it's safe to use qSort and foreach loops in my worker thread right? The reason I ask about foreach is I heard iterators in Qt are not safe to use in multiple threads.

                        kshegunovK Offline
                        kshegunovK Offline
                        kshegunov
                        Moderators
                        wrote on last edited by kshegunov
                        #11

                        Quick follow-up question - it's safe to use qSort and foreach loops in my worker thread right?

                        Only if the object isn't shared between threads. Usual rules for reentrant data apply - one can have only one thread writing (edit: without concurrent reads) at any one time. If your object is used from only one thread, then yes, it's safe, if not you need to serialize access (think a QMutex).

                        Read and abide by the Qt Code of Conduct

                        C 1 Reply Last reply
                        2
                        • kshegunovK kshegunov

                          Quick follow-up question - it's safe to use qSort and foreach loops in my worker thread right?

                          Only if the object isn't shared between threads. Usual rules for reentrant data apply - one can have only one thread writing (edit: without concurrent reads) at any one time. If your object is used from only one thread, then yes, it's safe, if not you need to serialize access (think a QMutex).

                          C Offline
                          C Offline
                          Crag_Hack
                          wrote on last edited by
                          #12

                          Thanks kshegunov - that applies to both qsort and foreach loops right?

                          kshegunovK JonBJ 2 Replies Last reply
                          0
                          • C Crag_Hack

                            Thanks kshegunov - that applies to both qsort and foreach loops right?

                            kshegunovK Offline
                            kshegunovK Offline
                            kshegunov
                            Moderators
                            wrote on last edited by
                            #13

                            Naturally, yes.

                            Read and abide by the Qt Code of Conduct

                            1 Reply Last reply
                            0
                            • C Crag_Hack

                              Thanks kshegunov - that applies to both qsort and foreach loops right?

                              JonBJ Offline
                              JonBJ Offline
                              JonB
                              wrote on last edited by
                              #14

                              @Crag_Hack said in Non-Reentrant Class Use In Multithreaded Program:

                              Thanks kshegunov - that applies to both qsort and foreach loops right?

                              Just to be clear, it applies to everything you might write, whether that be foreach, qsort or anything else.

                              1 Reply Last reply
                              2
                              • JonBJ Offline
                                JonBJ Offline
                                JonB
                                wrote on last edited by
                                #15

                                @kshegunov
                                I have two questions about using the QMutex pattern to synchronise reads with writes, please:

                                1. I assume that if the reader wants to read just one item it must still lock the whole object and the writer must lock the whole object, as the reader/iterator could be thrown by the write creating/deleting an item in the middle of the data?

                                2. Given the above, what pattern would we use to allow multiple-simultaneous-readers exclusive from single-writer? A reader would still need to grab the QMutex to prevent a writer, but we don't want to stop a second reader which would also want the QMutex?

                                kshegunovK 1 Reply Last reply
                                0
                                • JonBJ JonB

                                  @kshegunov
                                  I have two questions about using the QMutex pattern to synchronise reads with writes, please:

                                  1. I assume that if the reader wants to read just one item it must still lock the whole object and the writer must lock the whole object, as the reader/iterator could be thrown by the write creating/deleting an item in the middle of the data?

                                  2. Given the above, what pattern would we use to allow multiple-simultaneous-readers exclusive from single-writer? A reader would still need to grab the QMutex to prevent a writer, but we don't want to stop a second reader which would also want the QMutex?

                                  kshegunovK Offline
                                  kshegunovK Offline
                                  kshegunov
                                  Moderators
                                  wrote on last edited by kshegunov
                                  #16

                                  These are relatively hard questions to answer, because it will depend on the way the data's organized. I'd make an effort to elaborate the best I can, nonetheless.

                                  For 1:

                                  Race conditions are an inevitable consequence of memory access, so if a race condition occurs depends heavily on the memory layout of the object/memory block in question. Say we are talking about a regular reentrant object, then it is safe in theory to call two methods simultaneously (i.e. from different threads) provided the mentioned two methods work on a different data member of that class. For example, if you have the very simple:

                                  struct MyStruct
                                  {
                                      int a, b;
                                  };
                                  

                                  Then you can completely safely read or modify a from one thread and b from from another as long as you don't touch the other member. The same considerations apply to plain C arrays of data, e.g.:

                                  int * myarray = new int[200];
                                  

                                  Then you can read or modify simultaneously from different threads different parts of the array; meaning you can write to the first element while reading the second from different threads safely. If you can guarantee there's no overlap between the memory accesses made from the different threads, then there's no race condition, and you're completely fine. This is exploited, for example, if you have the consumer-producer problem and there's an example in Qt's documentation of how it can be achieved with semaphores. To make your regular objects work like this, however, is often too finicky and error prone, so you'd usually opt for a serial access (i.e. through a mutex). Also see answer to question #2 for related information.

                                  Now, if you use a container (for example std::vector or especially QVector due to implicit sharing) it's a bit more complicated. If you're working with std::vector you're fine obtaining a read or write iterator directly, because there's no data sharing behind the scenes. Then the only problem to solve is like with regular arrays - to ensure there are no concurrent memory accesses of the different elements. If you use QVector (or a QList, QHash, etc.) on the other hand then obtaining the write iterator is by itself a write operation, because the data may be shared and data copy might occur at that point. After the possible detach has happened then the same considerations as for std::vector apply.

                                  For 2:
                                  If you want to permit multiple readers and writers to access a specific data block, then a more elaborate scheme for locking is needed. As you correctly observed mutexes are exclusive, hence the name - mutual exclusive lock. They also guard a single resource, for example the whole array. If you need to have a threading primitive that provides guarding of multiple resources, then you need a semaphore (like in the consumer-producer example sourced above). On the other hand if you're satisfied with exclusive access only for write-induced races, which is exactly your question - allowing a single write at one time, but simultaneous reads, you could use Qt's QReadWriteLock which provides a locking scheme for that specific case (internal implementation isn't that important here, so I'll not venture into it).

                                  Here I also would like to note that if you can prepopulate the data, then it could be a very viable approach to avoid needless locking. Because after you have the data in a container (provided it holds reentrant objects and the classes respect the method's const modifier, meaning no const_cast or mutable), you could use const-only access to that container and data in as many threads as you need. I quite often do that in my work with hashes.

                                  Read and abide by the Qt Code of Conduct

                                  JonBJ VRoninV 2 Replies Last reply
                                  3
                                  • kshegunovK kshegunov

                                    These are relatively hard questions to answer, because it will depend on the way the data's organized. I'd make an effort to elaborate the best I can, nonetheless.

                                    For 1:

                                    Race conditions are an inevitable consequence of memory access, so if a race condition occurs depends heavily on the memory layout of the object/memory block in question. Say we are talking about a regular reentrant object, then it is safe in theory to call two methods simultaneously (i.e. from different threads) provided the mentioned two methods work on a different data member of that class. For example, if you have the very simple:

                                    struct MyStruct
                                    {
                                        int a, b;
                                    };
                                    

                                    Then you can completely safely read or modify a from one thread and b from from another as long as you don't touch the other member. The same considerations apply to plain C arrays of data, e.g.:

                                    int * myarray = new int[200];
                                    

                                    Then you can read or modify simultaneously from different threads different parts of the array; meaning you can write to the first element while reading the second from different threads safely. If you can guarantee there's no overlap between the memory accesses made from the different threads, then there's no race condition, and you're completely fine. This is exploited, for example, if you have the consumer-producer problem and there's an example in Qt's documentation of how it can be achieved with semaphores. To make your regular objects work like this, however, is often too finicky and error prone, so you'd usually opt for a serial access (i.e. through a mutex). Also see answer to question #2 for related information.

                                    Now, if you use a container (for example std::vector or especially QVector due to implicit sharing) it's a bit more complicated. If you're working with std::vector you're fine obtaining a read or write iterator directly, because there's no data sharing behind the scenes. Then the only problem to solve is like with regular arrays - to ensure there are no concurrent memory accesses of the different elements. If you use QVector (or a QList, QHash, etc.) on the other hand then obtaining the write iterator is by itself a write operation, because the data may be shared and data copy might occur at that point. After the possible detach has happened then the same considerations as for std::vector apply.

                                    For 2:
                                    If you want to permit multiple readers and writers to access a specific data block, then a more elaborate scheme for locking is needed. As you correctly observed mutexes are exclusive, hence the name - mutual exclusive lock. They also guard a single resource, for example the whole array. If you need to have a threading primitive that provides guarding of multiple resources, then you need a semaphore (like in the consumer-producer example sourced above). On the other hand if you're satisfied with exclusive access only for write-induced races, which is exactly your question - allowing a single write at one time, but simultaneous reads, you could use Qt's QReadWriteLock which provides a locking scheme for that specific case (internal implementation isn't that important here, so I'll not venture into it).

                                    Here I also would like to note that if you can prepopulate the data, then it could be a very viable approach to avoid needless locking. Because after you have the data in a container (provided it holds reentrant objects and the classes respect the method's const modifier, meaning no const_cast or mutable), you could use const-only access to that container and data in as many threads as you need. I quite often do that in my work with hashes.

                                    JonBJ Offline
                                    JonBJ Offline
                                    JonB
                                    wrote on last edited by
                                    #17

                                    @kshegunov
                                    First, thanks for your comprehensive explanation.

                                    For #1, you're making it way more complex than I had in mind. Say I have one thread writer & one thread reader, and my data is, say, a list/array of integers (forget vectors), no structures or whatever. I wish to use QMutex. All I wanted to verify is: (a) writer can insert/delete/update list/array; (b) reader wants just to read element #10; (c) confirm that the implementation must be QMutex for whole object/list/array, so that writer cannot change elements while reader trying to read one element.

                                    For #2, QReadWriteLock is just the ticket instead of QMutex, thank you.

                                    kshegunovK 1 Reply Last reply
                                    0
                                    • JonBJ JonB

                                      @kshegunov
                                      First, thanks for your comprehensive explanation.

                                      For #1, you're making it way more complex than I had in mind. Say I have one thread writer & one thread reader, and my data is, say, a list/array of integers (forget vectors), no structures or whatever. I wish to use QMutex. All I wanted to verify is: (a) writer can insert/delete/update list/array; (b) reader wants just to read element #10; (c) confirm that the implementation must be QMutex for whole object/list/array, so that writer cannot change elements while reader trying to read one element.

                                      For #2, QReadWriteLock is just the ticket instead of QMutex, thank you.

                                      kshegunovK Offline
                                      kshegunovK Offline
                                      kshegunov
                                      Moderators
                                      wrote on last edited by
                                      #18

                                      @JNBarchan said in Non-Reentrant Class Use In Multithreaded Program:

                                      confirm that the implementation must be QMutex for whole object/list/array, so that writer cannot change elements while reader trying to read one element.

                                      Yes, I confirm. While the mutex is locked the whole object/list/array is owned by the locking thread. All other threads requesting access are waiting and one of them (in an unspecified order) will acquire the mutex when the owning thread unlocks it.

                                      Read and abide by the Qt Code of Conduct

                                      1 Reply Last reply
                                      2
                                      • kshegunovK kshegunov

                                        These are relatively hard questions to answer, because it will depend on the way the data's organized. I'd make an effort to elaborate the best I can, nonetheless.

                                        For 1:

                                        Race conditions are an inevitable consequence of memory access, so if a race condition occurs depends heavily on the memory layout of the object/memory block in question. Say we are talking about a regular reentrant object, then it is safe in theory to call two methods simultaneously (i.e. from different threads) provided the mentioned two methods work on a different data member of that class. For example, if you have the very simple:

                                        struct MyStruct
                                        {
                                            int a, b;
                                        };
                                        

                                        Then you can completely safely read or modify a from one thread and b from from another as long as you don't touch the other member. The same considerations apply to plain C arrays of data, e.g.:

                                        int * myarray = new int[200];
                                        

                                        Then you can read or modify simultaneously from different threads different parts of the array; meaning you can write to the first element while reading the second from different threads safely. If you can guarantee there's no overlap between the memory accesses made from the different threads, then there's no race condition, and you're completely fine. This is exploited, for example, if you have the consumer-producer problem and there's an example in Qt's documentation of how it can be achieved with semaphores. To make your regular objects work like this, however, is often too finicky and error prone, so you'd usually opt for a serial access (i.e. through a mutex). Also see answer to question #2 for related information.

                                        Now, if you use a container (for example std::vector or especially QVector due to implicit sharing) it's a bit more complicated. If you're working with std::vector you're fine obtaining a read or write iterator directly, because there's no data sharing behind the scenes. Then the only problem to solve is like with regular arrays - to ensure there are no concurrent memory accesses of the different elements. If you use QVector (or a QList, QHash, etc.) on the other hand then obtaining the write iterator is by itself a write operation, because the data may be shared and data copy might occur at that point. After the possible detach has happened then the same considerations as for std::vector apply.

                                        For 2:
                                        If you want to permit multiple readers and writers to access a specific data block, then a more elaborate scheme for locking is needed. As you correctly observed mutexes are exclusive, hence the name - mutual exclusive lock. They also guard a single resource, for example the whole array. If you need to have a threading primitive that provides guarding of multiple resources, then you need a semaphore (like in the consumer-producer example sourced above). On the other hand if you're satisfied with exclusive access only for write-induced races, which is exactly your question - allowing a single write at one time, but simultaneous reads, you could use Qt's QReadWriteLock which provides a locking scheme for that specific case (internal implementation isn't that important here, so I'll not venture into it).

                                        Here I also would like to note that if you can prepopulate the data, then it could be a very viable approach to avoid needless locking. Because after you have the data in a container (provided it holds reentrant objects and the classes respect the method's const modifier, meaning no const_cast or mutable), you could use const-only access to that container and data in as many threads as you need. I quite often do that in my work with hashes.

                                        VRoninV Offline
                                        VRoninV Offline
                                        VRonin
                                        wrote on last edited by VRonin
                                        #19

                                        @kshegunov said in Non-Reentrant Class Use In Multithreaded Program:

                                        and the classes respect the method's const modifier, meaning no const_cast or mutable

                                        Just a small note, the above is not exhaustive. For example:

                                        class BreakingConst{
                                        int* a;
                                        BreakingConst(int b=0) : a(new int(b)){}
                                        ~BreakingConst(){delete a;}
                                        int getIncreasingA() const{ return (*a)++;}
                                        };
                                        

                                        has no const_cast or mutable but still breaks if used as described

                                        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                                        ~Napoleon Bonaparte

                                        On a crusade to banish setIndexWidget() from the holy land of Qt

                                        kshegunovK 1 Reply Last reply
                                        2
                                        • VRoninV VRonin

                                          @kshegunov said in Non-Reentrant Class Use In Multithreaded Program:

                                          and the classes respect the method's const modifier, meaning no const_cast or mutable

                                          Just a small note, the above is not exhaustive. For example:

                                          class BreakingConst{
                                          int* a;
                                          BreakingConst(int b=0) : a(new int(b)){}
                                          ~BreakingConst(){delete a;}
                                          int getIncreasingA() const{ return (*a)++;}
                                          };
                                          

                                          has no const_cast or mutable but still breaks if used as described

                                          kshegunovK Offline
                                          kshegunovK Offline
                                          kshegunov
                                          Moderators
                                          wrote on last edited by kshegunov
                                          #20

                                          It's probably somewhat of a fringe use case, but yes, you are right. The const modifier doesn't implicitly propagate to the indirectly referenced data. a is of type int * const in that case, which allows you to actually dereference the pointer and modify the underlying data. The same behavior is true for a heap-allocated array too, however with auto-storage:

                                          class BreakingConst
                                          {
                                              int a[1];
                                          
                                              BreakingConst(int b=0) { a[0] = b; }
                                              ~BreakingConst()  { delete a; }
                                              int getIncreasingA() const  { return a[0]++; }
                                          };
                                          

                                          The compiler knows what's going on and will prevent it.

                                          Read and abide by the Qt Code of Conduct

                                          1 Reply Last reply
                                          1

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved