Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-81846

QSharedMemory/QSystemSemaphore should use named mutexes on Windows (auto-release on crash)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P3: Somewhat important
    • None
    • 4.4.0, 5.14.1
    • Core: Threads
    • None
    • Windows

    Description

      On Windows, QSharedMemory uses a Win32 semaphore to allow user to synchronize access to the region. As this Microsoft blog post says:

      "If the thread exits (or crashes) before it manages to release the semaphore, the semaphore counter is not automatically restored. Compare mutexes, where the mutex is released if the owner thread terminates while holding it. "https://devblogs.microsoft.com/oldnewthing/20051123-14/?p=33233

      This code snippet demonstrates this is prone to deadlocks:

      #include <qcoreapplication.h>
      #include <qsharedmemory.h>
      #include <qprocess.h>
      #include <iostream>
      
      using namespace std;
      QSharedMemory region;
      
      int parent(int argc, char** argv)
      {
          QCoreApplication app(argc, argv);
          QProcess process;
          QStringList args = { "child"};    
          cout << "I am the parent" << endl;
          region.setKey("myregion");
          region.create(1024);
          assert(region.isAttached());
          process.setProcessChannelMode(QProcess::ForwardedChannels);
          process.start("qsharedmemory_test", args);
          process.waitForFinished();
          region.lock(); // this will never return
          app.exec();
          return 0;
      }
      
      int child(int argc, char** argv)
      {
          QCoreApplication app(argc, argv);
          int* crash = nullptr;   
      
          cout << "I am the child" << endl;
          region.setKey("myregion");
          region.attach();
          region.lock();
          // intentionally crash
          *crash = 5;
          assert(region.isAttached());    
          app.exec();
          return 0;
      }
      
      int main(int argc, char** argv)
      {
          
          if (argc < 2) {
              cout << "Missing argument" << endl;
              exit(EXIT_FAILURE);
          }
          
          if (!strcmp(argv[1], "parent"))
              return parent(argc, argv);
          else if (!strcmp(argv[1], "child"))
              return child(argc, argv);
          else {
              cout << "Unexpected argument";
              exit(EXIT_FAILURE);
          }
      }
      
      

      The parent will deadlock when calling region.lock().

      For instance, in Boost you would use a named mutex to lock a shared memory region The mutex would not be placed in shared memory:

      https://stackoverflow.com/questions/56865554/how-to-release-boostinterprocessnamed-mutex-when-the-process-crashes/56865730

      "The whole point of using a named mutex is that multiple processes can create their own local mutex objects using the same name and they will share an underlying mutex that they can sync on. If a given process locks the mutex and then crashes, the underlying shared mutex will be released automatically by the OS, allowing another process to lock it (depending on OS, the underlying mutex API may report that the mutex had been unlocked "abnormally")

       

       

      Attachments

        1. main.cpp
          1 kB
        2. qsharedmemory_test.pro
          0.1 kB
        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            Unassigned Unassigned
            ghc Gerardo Hernandez
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes