Refactor CallerHandle / LauncherHandle

Fix a race condition in the following scenario:

  QtcProcess proc(...)
  ...
  proc.start();
  proc.waitForStarted();
  if (!proc.waitForFinished()) {
      // handle failure here
      return;
  }
  // handle success

Move all the data into the caller's handle
and manage the QtcProcess state only from
inside caller's thread. This eliminates race
conditions when state changed from inside launcher's
thread while the caller's thread isn't notified
immediately.

For example: currently, when the launcher's thread receives
finished signal it doesn't change the process state
immediately, but posts a finished signal to be
dispatched in the caller's thread. When the caller's
thread dispatches the posted signal (inside flush() method)
it changes its state and posts the finished signal to the
outside world.

Don't flush all signals from waitForStarted(). Flush
only started signal in this case.

Change-Id: Ia39c4021bf43b8d0e8fcda789c367c096bfd032c
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2021-08-23 17:58:44 +02:00
parent d932b8535a
commit 1a5db9ca4e
4 changed files with 750 additions and 630 deletions

View File

@@ -252,15 +252,15 @@ public:
: ProcessInterface(processMode), m_token(uniqueToken())
{
m_handle = LauncherInterface::socket()->registerHandle(token(), processMode);
connect(m_handle, &LauncherHandle::errorOccurred,
connect(m_handle, &CallerHandle::errorOccurred,
this, &ProcessInterface::errorOccurred);
connect(m_handle, &LauncherHandle::started,
connect(m_handle, &CallerHandle::started,
this, &ProcessInterface::started);
connect(m_handle, &LauncherHandle::finished,
connect(m_handle, &CallerHandle::finished,
this, &ProcessInterface::finished);
connect(m_handle, &LauncherHandle::readyReadStandardOutput,
connect(m_handle, &CallerHandle::readyReadStandardOutput,
this, &ProcessInterface::readyReadStandardOutput);
connect(m_handle, &LauncherHandle::readyReadStandardError,
connect(m_handle, &CallerHandle::readyReadStandardError,
this, &ProcessInterface::readyReadStandardError);
}
~ProcessLauncherImpl() override
@@ -318,7 +318,7 @@ private:
const uint m_token = 0;
// Lives in launcher's thread.
LauncherHandle *m_handle = nullptr;
CallerHandle *m_handle = nullptr;
};
void ProcessLauncherImpl::cancel()
@@ -456,6 +456,9 @@ QtcProcess::QtcProcess(ProcessImpl processImpl, ProcessMode processMode, QObject
Q_UNUSED(qProcessProcessErrorMeta)
}
QtcProcess::QtcProcess(ProcessImpl processImpl, QObject *parent)
: QtcProcess(processImpl, ProcessMode::Reader, parent) {}
QtcProcess::QtcProcess(ProcessMode processMode, QObject *parent)
: QtcProcess(defaultProcessImpl(), processMode, parent) {}